├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql.yml │ ├── prerelease.yaml │ └── releases.yaml ├── .gitignore ├── BappDescription.html ├── BappManifest.bmf ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── aliases.png ├── colorfilters.png ├── filters.png └── grep.png ├── settings.gradle └── src ├── main ├── java │ └── com │ │ └── nccgroup │ │ └── loggerplusplus │ │ ├── LoggerContextMenuFactory.java │ │ ├── LoggerPlusPlus.java │ │ ├── MainViewController.java │ │ ├── about │ │ └── AboutPanel.java │ │ ├── exports │ │ ├── AutomaticLogExporter.java │ │ ├── Base64Exporter.java │ │ ├── CSVExporter.java │ │ ├── CSVExporterControlPanel.java │ │ ├── ContextMenuExportProvider.java │ │ ├── ElasticExporter.java │ │ ├── ElasticExporterConfigDialog.java │ │ ├── ElasticExporterControlPanel.java │ │ ├── ExportController.java │ │ ├── ExportPanelProvider.java │ │ ├── HARExporter.java │ │ ├── HARExporterControlPanel.java │ │ ├── HarSerializer.java │ │ ├── JSONExporter.java │ │ ├── JSONExporterControlPanel.java │ │ └── LogExporter.java │ │ ├── filter │ │ ├── ColorizingFilterRule.java │ │ ├── ComparisonOperator.java │ │ ├── FilterExpression.java │ │ ├── FilterExpressionSerializer.java │ │ ├── FilterRule.java │ │ ├── LogicalOperator.java │ │ ├── SavedFilterSerializer.java │ │ ├── TableColorRuleSerializer.java │ │ ├── TagSerializer.java │ │ ├── colorfilter │ │ │ ├── ColorFilterListener.java │ │ │ └── TableColorRule.java │ │ ├── logfilter │ │ │ ├── LogFilterController.java │ │ │ └── LogTableFilter.java │ │ ├── parser │ │ │ ├── ASTAlias.java │ │ │ ├── ASTComparison.java │ │ │ ├── ASTExpression.java │ │ │ ├── AliasCheckVisitor.java │ │ │ ├── Filter.jj │ │ │ ├── Filter.jjt │ │ │ ├── FilterEvaluationVisitor.java │ │ │ ├── FilterParser.java │ │ │ ├── FilterParserConstants.java │ │ │ ├── FilterParserDefaultVisitor.java │ │ │ ├── FilterParserTokenManager.java │ │ │ ├── FilterParserTreeConstants.java │ │ │ ├── FilterParserVisitor.java │ │ │ ├── JJTFilterParserState.java │ │ │ ├── Node.java │ │ │ ├── ParseException.java │ │ │ ├── SanityCheckVisitor.java │ │ │ ├── SimpleCharStream.java │ │ │ ├── SimpleNode.java │ │ │ ├── Token.java │ │ │ ├── TokenMgrError.java │ │ │ └── VisitorData.java │ │ ├── savedfilter │ │ │ └── SavedFilter.java │ │ └── tag │ │ │ ├── Tag.java │ │ │ └── TagListener.java │ │ ├── filterlibrary │ │ ├── FilterLibraryController.java │ │ ├── FilterLibraryListener.java │ │ ├── FilterLibraryPanel.java │ │ └── FilterLibraryTableModel.java │ │ ├── grepper │ │ ├── GrepResults.java │ │ ├── GrepResultsTable.java │ │ ├── GrepperController.java │ │ ├── GrepperListener.java │ │ ├── GrepperPanel.java │ │ └── UniquePatternMatchTable.java │ │ ├── help │ │ └── HelpPanel.java │ │ ├── imports │ │ └── LoggerImport.java │ │ ├── logentry │ │ ├── FieldGroup.java │ │ ├── LogEntry.java │ │ ├── LogEntryField.java │ │ ├── LogEntryFieldSerializer.java │ │ ├── LogEntrySerializer.java │ │ └── Status.java │ │ ├── logging │ │ ├── BurpAppender.java │ │ └── LoggingController.java │ │ ├── logview │ │ ├── LogTableFilterStatusListener.java │ │ ├── LogViewController.java │ │ ├── LogViewPanel.java │ │ ├── MainControlsPanel.java │ │ ├── MultipleLogEntryMenu.java │ │ ├── SingleLogEntryMenu.java │ │ ├── entryviewer │ │ │ ├── RequestViewerController.java │ │ │ └── RequestViewerPanel.java │ │ ├── logtable │ │ │ ├── LogTable.java │ │ │ ├── LogTableColumn.java │ │ │ ├── LogTableColumnModel.java │ │ │ ├── LogTableController.java │ │ │ ├── LogTableModel.java │ │ │ ├── TableHeader.java │ │ │ └── TableHeaderMenu.java │ │ └── processor │ │ │ ├── EntryImportWorker.java │ │ │ ├── LogProcessor.java │ │ │ └── LogProcessorHelper.java │ │ ├── preferences │ │ ├── LoggerPreferenceFactory.java │ │ ├── PreferencesController.java │ │ └── PreferencesPanel.java │ │ ├── reflection │ │ ├── ReflectionController.java │ │ ├── filter │ │ │ ├── BlacklistFilter.java │ │ │ ├── LengthFilter.java │ │ │ └── ParameterFilter.java │ │ └── transformer │ │ │ ├── Base64DecodeTransformer.java │ │ │ ├── Base64EncodeTransformer.java │ │ │ ├── HTMLEscapeTransformer.java │ │ │ ├── HTMLUnescapeTransformer.java │ │ │ ├── HexEncodeTransformer.java │ │ │ ├── JsonEscapeTransformer.java │ │ │ ├── JsonUnescapeTransformer.java │ │ │ ├── ParameterValueTransformer.java │ │ │ ├── URLDecodeTransformer.java │ │ │ ├── URLEncodeTransformer.java │ │ │ ├── XMLEscapeTransformer.java │ │ │ └── XMLUnescapeTransformer.java │ │ └── util │ │ ├── FieldSelectorDialog.java │ │ ├── Globals.java │ │ ├── MoreHelp.java │ │ ├── NamedThreadFactory.java │ │ ├── PausableThreadPoolExecutor.java │ │ ├── SwingWorkerWithProgressDialog.java │ │ └── userinterface │ │ ├── ColorEditor.java │ │ ├── LoggerMenu.java │ │ ├── NoTextSelectionCaret.java │ │ ├── ScrollablePanel.java │ │ ├── WrappedTextPane.java │ │ ├── dialog │ │ ├── ColorFilterDialog.java │ │ ├── ColorFilterTable.java │ │ ├── ColorFilterTableModel.java │ │ ├── TagDialog.java │ │ ├── TagTable.java │ │ └── TagTableModel.java │ │ └── renderer │ │ ├── BooleanRenderer.java │ │ ├── ButtonRenderer.java │ │ ├── ColorRenderer.java │ │ ├── FilterRenderer.java │ │ ├── LeftTableCellRenderer.java │ │ └── TagRenderer.java └── resources │ ├── AboutMain.png │ ├── GitHubLogoBlack.png │ ├── GitHubLogoWhite.png │ ├── NCCGroup.png │ ├── NCCLarge.png │ ├── TwitterLogo.png │ └── log4j2.xml └── test └── java └── Test.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Help improve Logger++ 4 | title: '' 5 | labels: bug 6 | assignees: CoreyD97 7 | 8 | --- 9 | 10 | **Description:** 11 | 12 | **Steps To Reproduce:** 13 | 14 | 15 | **Expected behavior:** 16 | 17 | 18 | **Screenshots:** 19 | 20 | **Version:** 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Help make Logger++ awesome! 4 | title: '' 5 | labels: enhancement 6 | assignees: CoreyD97 7 | 8 | --- 9 | 10 | **Feature description:** 11 | I want... 12 | 13 | **Scenario example:** 14 | This would... (make it easier to write filters, help export logs to other tools, etc) 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ 'master' ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ 'master' ] 9 | schedule: 10 | - cron: '30 11 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'java' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 26 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v3 31 | 32 | - name: Set up Java 33 | uses: actions/setup-java@v2 34 | with: 35 | distribution: adopt 36 | java-version: '17' 37 | 38 | # Initializes the CodeQL tools for scanning. 39 | - name: Initialize CodeQL 40 | uses: github/codeql-action/init@v2 41 | with: 42 | languages: ${{ matrix.language }} 43 | # If you wish to specify custom queries, you can do so here or in a config file. 44 | # By default, queries listed here will override any specified in a config file. 45 | # Prefix the list here with "+" to use these queries and those in the config file. 46 | 47 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 48 | queries: +security-and-quality 49 | 50 | 51 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 52 | # If this step fails, then you should remove it and run the build manually (see below) 53 | - name: Autobuild 54 | uses: github/codeql-action/autobuild@v2 55 | 56 | # ℹ️ Command-line programs to run using the OS shell. 57 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 58 | 59 | # If the Autobuild fails above, remove it and uncomment the following three lines. 60 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 61 | 62 | # - run: | 63 | # echo "Run, Build Application using script" 64 | # ./location_of_script_within_repo/buildscript.sh 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v2 68 | with: 69 | category: "/language:${{matrix.language}}" 70 | -------------------------------------------------------------------------------- /.github/workflows/prerelease.yaml: -------------------------------------------------------------------------------- 1 | name: "pre-release" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout source code 13 | uses: actions/checkout@v2.3.4 14 | with: 15 | lfs: true 16 | fetch-depth: 0 17 | 18 | - name: Set up Java 19 | uses: actions/setup-java@v2 20 | with: 21 | distribution: adopt 22 | java-version: '17' 23 | 24 | # burpsuite_pro.jar is not available, disable tests 25 | - name: Build 26 | run: ./gradlew build -x test 27 | 28 | - uses: "marvinpinto/action-automatic-releases@latest" 29 | with: 30 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 31 | prerelease: true 32 | automatic_release_tag: "latest" 33 | files: | 34 | releases/*.jar 35 | id: "automatic_releases" 36 | -------------------------------------------------------------------------------- /.github/workflows/releases.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: releases 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v*" 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout source code 14 | uses: actions/checkout@v2.3.4 15 | with: 16 | lfs: true 17 | fetch-depth: 0 18 | 19 | - name: Set up Java 20 | uses: actions/setup-java@v2 21 | with: 22 | distribution: adopt 23 | java-version: '17' 24 | 25 | # burpsuite_pro.jar is not available, disable tests 26 | - name: Build 27 | run: ./gradlew build -x test 28 | 29 | - uses: "marvinpinto/action-automatic-releases@latest" 30 | with: 31 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 32 | prerelease: false 33 | files: | 34 | releases/*.jar 35 | id: "automatic_releases" 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .*/ 3 | build/ 4 | out/ 5 | Util/ 6 | libs/burpsuite_pro.jar 7 | BurpLogger++.iml 8 | TODO 9 | .classpath 10 | .project 11 | build.xml 12 | hs_err_*.log 13 | .vscode/**/* 14 | releases/**/* 15 | .settings/ -------------------------------------------------------------------------------- /BappDescription.html: -------------------------------------------------------------------------------- 1 |

Logger++ is a multithreaded logging extension for Burp Suite. In addition to logging requests and responses from all Burp Suite tools, the extension allows advanced filters to be defined to highlight interesting entries or filter logs to only those which match the filter.

2 | 3 |

A built in grep tool allows the logs to be searched to locate entries which match a specified pattern, and extract the values of the capture groups.

4 | 5 |

To enable logs to be used in other systems, the table can also be uploaded to elasticsearch or exported to CSV.

6 | 7 |

Features:

8 | 9 | 24 | -------------------------------------------------------------------------------- /BappManifest.bmf: -------------------------------------------------------------------------------- 1 | Uuid: 470b7057b86f41c396a97903377f3d81 2 | ExtensionType: 1 3 | Name: Logger++ 4 | RepoName: logger-plus-plus 5 | ScreenVersion: 3.20.0 6 | SerialVersion: 22 7 | MinPlatformVersion: 8 8 | ProOnly: False 9 | Author: Corey Arthur, NCC Group 10 | ShortDescription: Logs requests and responses for all Burp tools in a sortable table. 11 | EntryPoint: releases/LoggerPlusPlus.jar 12 | BuildCommand: ./gradlew jar 13 | SupportedProducts: Pro, Community 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Logger++

2 |

Advanced Logging for Burp Suite

3 |

4 | 5 | GitHub Watchers 6 | GitHub Stars 7 | GitHub All Releases 8 | GitHub License 9 |

10 | 11 | Developed by Corey 12 | Arthur [![Twitter Follow](https://img.shields.io/badge/follow-%40CoreyD97-1DA1F2?logo=twitter&style=social)](https://twitter.com/coreyd97/) 13 | Originally by Soroush 14 | Dalili [![Twitter Follow](https://img.shields.io/badge/follow-%40irsdl-1DA1F2?logo=twitter&style=social)](https://twitter.com/irsdl/) 15 | 16 | Released as open source by NCC Group Plc - https://www.nccgroup.com/ 17 | Released under AGPL-3.0 see LICENSE for more information 18 | 19 | Description 20 | ---- 21 | 22 | Logger++ is a multithreaded logging extension for Burp Suite. In addition to logging requests and responses from all 23 | Burp Suite tools, the extension allows advanced filters to be defined to highlight interesting entries or filter logs to 24 | only those which match the filter. 25 | 26 | A built in grep tool allows the logs to be searched to locate entries which match a specified pattern, and extract the 27 | values of the capture groups. 28 | 29 | To enable logs to be used in other systems, the table can also be uploaded to elasticsearch or exported to CSV. 30 | 31 | Features: 32 | 33 | - Works with the latest version of Burp Suite (tested on 1.7.27) 34 | - Logs all the tools that are sending requests and receiving responses 35 | - Ability to log from a specific tool 36 | - Ability to save the results in CSV format 37 | - Ability to show results of custom regular expressions in request/response 38 | - User can customise the column headers 39 | - Advanced Filters can be created to display only requests matching a specific string or regex pattern. 40 | - Row highlighting can be added using advanced filters to make interesting requests more visible. 41 | - Grep through logs. 42 | - Live requests and responses. 43 | - Multiple view options. 44 | - Pop out view panel. 45 | - Multithreaded. 46 | 47 | Current Limitations: 48 | 49 | - Cannot log the requests' actual time unless originating from proxy tool. 50 | - Cannot calculate the actual delay between a request and its response unless originating from proxy tool. 51 | 52 | Screenshots 53 | ---------------------- 54 | 55 | Log Filters 56 | 57 | ![Log Filters](images/filters.png) 58 | 59 | Row Highlights 60 | 61 | ![Row Highlights](images/colorfilters.png) 62 | 63 | Grep Search 64 | 65 | ![Grep Panel](images/grep.png) 66 | 67 | 68 | Usage 69 | ---- 70 | You can use this extension without using the BApp store. In order to install the latest version of this extension from 71 | the GitHub repository, follow these steps: 72 | 73 | 1. Download the [latest release jar](https://github.com/nccgroup/LoggerPlusPlus/releases/latest) . 74 | 75 | 2. In Burp Suite, click on the "Extender" tab, then in the "Extensions" tab click on the "Add" button and select the 76 | downloaded "loggerplusplus.jar" file. 77 | 78 | 3. You should now be able to see the "Logger++" tab in Burp Suite. If it cannot log anything, check your Burp Suite 79 | extension settings. If the save buttons are disabled, make sure that the requested libraries have been loaded 80 | successfully; Unload and then reload the extension and try again. If you have found an issue, please report it in the 81 | GitHub project. 82 | 83 | 4. You can configure this extension by using its "option" tab and by right click on the columns' headers. 84 | 85 | 5. If you like the project, give the repo a star! <3 86 | 87 | ![Stargazers](https://starchart.cc/nccgroup/LoggerPlusPlus.svg) 88 | 89 | Contributing 90 | ---- 91 | 92 | ### Building from Source 93 | 94 | If you'd like to build the project from source, the project uses Gradle to simplify the process: 95 | 96 | 1. Clone the repository: `git clone https://github.com/nccgroup/LoggerPlusPlus.git` 97 | 2. Within the project folder, execute `gradlew jar` (Linux) `gradlew.bat jar` (Windows) 98 | 3. Once complete, you can find the built Jar in the project's `releases` folder. 99 | 100 | ### Testing 101 | 102 | IntelliJ Idea has been used to develop the project, but feel free to use any IDE you prefer. The following instructions 103 | are for Idea, but the process will be similar for other IDEs. 104 | 105 | 1. Within Idea, click `File > Open`, then select the project's `build.gradle` file. 106 | 2. Click "Open as Project" in the next dialog. 107 | 3. To run/debug the application, navigate to `Run > Edit Configurations`, then add a new "Application" configuration 108 | targeting the `TestLogger` class. 109 | 110 | ### Reporting bugs 111 | 112 | If you have found an issue, please report it via [GitHub](https://github.com/nccgroup/LoggerPlusPlus/issues/new/choose). 113 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id "io.freefair.lombok" version "6.5.1" 4 | } 5 | 6 | sourceCompatibility = JavaVersion.VERSION_17 7 | targetCompatibility = JavaVersion.VERSION_17 8 | 9 | repositories { 10 | mavenCentral() 11 | maven { 12 | url "https://jitpack.io" 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation 'net.portswigger.burp.extensions:montoya-api:2023.5' 18 | implementation 'org.swinglabs:swingx:1.6.1' 19 | implementation 'com.github.CoreyD97:Burp-Montoya-Utilities:234d21d' 20 | // implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.17.9' 21 | implementation 'co.elastic.clients:elasticsearch-java:8.6.2' 22 | implementation 'org.apache.httpcomponents:httpclient:4.5.13' 23 | implementation 'org.apache.commons:commons-text:1.10.0' 24 | implementation 'org.apache.logging.log4j:log4j-core:2.19.0' 25 | 26 | testRuntimeOnly files("${System.properties['user.home']}/BurpSuitePro/burpsuite_pro.jar") 27 | } 28 | 29 | jar { 30 | baseName = project.name 31 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 32 | 33 | from { 34 | (configurations.runtimeClasspath).collect { it.isDirectory() ? it : zipTree(it) } 35 | } { 36 | exclude "META-INF/*.SF" 37 | exclude "META-INF/*.DSA" 38 | exclude "META-INF/*.RSA" 39 | } 40 | } 41 | 42 | tasks.withType(Jar) { 43 | destinationDir = file("$rootDir/releases") 44 | } 45 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /images/aliases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/images/aliases.png -------------------------------------------------------------------------------- /images/colorfilters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/images/colorfilters.png -------------------------------------------------------------------------------- /images/filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/images/filters.png -------------------------------------------------------------------------------- /images/grep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/images/grep.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'LoggerPlusPlus' 2 | 3 | //if(file("../BurpExtenderUtilities").exists()){ 4 | // includeBuild('../BurpExtenderUtilities'){ 5 | // dependencySubstitution{ 6 | // substitute module('com.github.CoreyD97:BurpExtenderUtilities') with project(':') 7 | // } 8 | // } 9 | //} -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/MainViewController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.PopOutPanel; 4 | import com.nccgroup.loggerplusplus.about.AboutPanel; 5 | import com.nccgroup.loggerplusplus.help.HelpPanel; 6 | import com.nccgroup.loggerplusplus.util.Globals; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | public class MainViewController { 12 | 13 | private final JTabbedPane tabbedPane; 14 | private final PopOutPanel popOutWrapper; 15 | 16 | public MainViewController() { 17 | this.tabbedPane = new JTabbedPane(); 18 | LoggerPlusPlus loggerPlusPlus = LoggerPlusPlus.instance; 19 | tabbedPane.addTab("View Logs", null, loggerPlusPlus.getLogViewController().getLogViewPanel(), null); 20 | tabbedPane.addTab("Filter Library", null, loggerPlusPlus.getLibraryController().getFilterLibraryPanel(), null); 21 | tabbedPane.addTab("Grep Values", null, loggerPlusPlus.getGrepperController().getGrepperPanel(), null); 22 | tabbedPane.addTab("Options", null, loggerPlusPlus.getPreferencesController().getPreferencesPanel(), null); 23 | tabbedPane.addTab("About", null, new AboutPanel(loggerPlusPlus.getPreferencesController().getPreferences()), null); 24 | tabbedPane.addTab("Help", null, new HelpPanel(), null); 25 | this.popOutWrapper = new PopOutPanel(LoggerPlusPlus.montoya, tabbedPane, Globals.APP_NAME); 26 | } 27 | 28 | public Component getUiComponent() { 29 | return popOutWrapper; 30 | } 31 | 32 | public JTabbedPane getTabbedPanel(){ 33 | return tabbedPane; 34 | } 35 | 36 | public PopOutPanel getPopOutWrapper() { 37 | return popOutWrapper; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/AutomaticLogExporter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 5 | 6 | public abstract class AutomaticLogExporter extends LogExporter { 7 | 8 | protected AutomaticLogExporter(ExportController exportController, Preferences preferences){ 9 | super(exportController, preferences); 10 | } 11 | 12 | /** 13 | * Configure the exporter ready for use 14 | * @throws Exception Setup not completed 15 | */ 16 | abstract void setup() throws Exception; 17 | 18 | /** 19 | * Handle the export of a received entry 20 | * @param logEntry 21 | */ 22 | abstract void exportNewEntry(LogEntry logEntry); 23 | 24 | /** 25 | * Handle the export of a received entry 26 | * @param logEntry 27 | */ 28 | abstract void exportUpdatedEntry(LogEntry logEntry); 29 | 30 | /** 31 | * Clean up the exporter and its resources 32 | * @throws Exception 33 | */ 34 | abstract void shutdown() throws Exception; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/Base64Exporter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.google.gson.Gson; 5 | import com.google.gson.JsonObject; 6 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 7 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 8 | import com.nccgroup.loggerplusplus.util.MoreHelp; 9 | import com.nccgroup.loggerplusplus.util.SwingWorkerWithProgressDialog; 10 | 11 | import javax.swing.*; 12 | import java.awt.event.ActionEvent; 13 | import java.io.File; 14 | import java.io.FileWriter; 15 | import java.util.ArrayList; 16 | import java.util.Base64; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by corey on 21/08/17. 21 | */ 22 | public class Base64Exporter extends LogExporter implements ContextMenuExportProvider { 23 | 24 | public Base64Exporter(ExportController exportController, Preferences preferences) { 25 | super(exportController, preferences); 26 | } 27 | 28 | public void exportEntries(List entries, boolean includeRequest, boolean includeResponse) { 29 | if (!includeRequest && !includeResponse) 30 | throw new IllegalArgumentException("Must include either request, response or both."); 31 | try { 32 | File file = MoreHelp.getSaveFile("LoggerPlusPlus_Base64.json", "JSON Format", "json"); 33 | if (file.exists() && !MoreHelp.shouldOverwriteExistingFilePrompt()) return; 34 | 35 | SwingWorkerWithProgressDialog importWorker = new SwingWorkerWithProgressDialog( 36 | LoggerPlusPlus.instance.getLoggerFrame(), 37 | "Base64 Encoded JSON Export", "Exporting as Base64 encoded JSON...", entries.size()) { 38 | @Override 39 | protected Void doInBackground() throws Exception { 40 | super.doInBackground(); 41 | try (FileWriter fileWriter = new FileWriter(file, false)) { 42 | Gson gson = LoggerPlusPlus.gsonProvider.getGson(); 43 | ArrayList jsonEntries = new ArrayList<>(); 44 | Base64.Encoder encoder = Base64.getEncoder(); 45 | for (LogEntry entry : entries) { 46 | JsonObject jsonEntry = new JsonObject(); 47 | if (includeRequest) { 48 | jsonEntry.addProperty("request", 49 | encoder.encodeToString(entry.getRequestBytes())); 50 | } 51 | 52 | if (includeResponse) { 53 | jsonEntry.addProperty("response", 54 | encoder.encodeToString(entry.getResponseBytes())); 55 | } 56 | jsonEntries.add(jsonEntry); 57 | } 58 | 59 | gson.toJson(jsonEntries, fileWriter); 60 | } 61 | 62 | return null; 63 | } 64 | 65 | @Override 66 | protected void done() { 67 | super.done(); 68 | JOptionPane.showMessageDialog(LoggerPlusPlus.instance.getLoggerFrame(), 69 | "Export as Base64 completed.", 70 | "Base64 Export", JOptionPane.INFORMATION_MESSAGE); 71 | } 72 | }; 73 | 74 | importWorker.execute(); 75 | 76 | } catch (Exception e) { 77 | //Cancelled. 78 | } 79 | } 80 | 81 | @Override 82 | public JMenuItem getExportEntriesMenuItem(List entries) { 83 | JMenu parent = new JMenu(String.format("Export %d %s as Base64 (JSON Formatted)", 84 | entries.size(), entries.size() != 1 ? "entries" : "entry")); 85 | 86 | parent.add(new JMenuItem(new AbstractAction(entries.size() == 1 ? "Request Only" : "Requests Only") { 87 | @Override 88 | public void actionPerformed(ActionEvent e) { 89 | exportEntries(entries, true, false); 90 | } 91 | })); 92 | 93 | parent.add(new JMenuItem(new AbstractAction(entries.size() == 1 ? "Response Only" : "Responses Only") { 94 | @Override 95 | public void actionPerformed(ActionEvent e) { 96 | exportEntries(entries, false, true); 97 | } 98 | })); 99 | 100 | parent.add(new JMenuItem(new AbstractAction(entries.size() == 1 ? "Request and Response" : "Requests and Responses") { 101 | @Override 102 | public void actionPerformed(ActionEvent e) { 103 | exportEntries(entries, true, true); 104 | } 105 | })); 106 | 107 | return parent; 108 | } 109 | 110 | public ExportController getExportController() { 111 | return this.exportController; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/CSVExporterControlPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.awt.event.ActionEvent; 11 | import java.util.List; 12 | 13 | public class CSVExporterControlPanel extends JPanel { 14 | 15 | private final CSVExporter csvExporter; 16 | 17 | CSVExporterControlPanel(CSVExporter csvExporter){ 18 | this.csvExporter = csvExporter; 19 | this.setLayout(new BorderLayout()); 20 | 21 | JButton manualSaveButton = new JButton("Export as CSV"); 22 | manualSaveButton.addActionListener(actionEvent -> { 23 | final List entries = LoggerPlusPlus.instance.getLogEntries(); 24 | csvExporter.exportEntries(entries); 25 | }); 26 | 27 | JToggleButton exportButton = new JToggleButton("Auto-export as CSV"); 28 | exportButton.addActionListener(new AbstractAction() { 29 | @Override 30 | public void actionPerformed(ActionEvent actionEvent) { 31 | boolean newSelectedState = exportButton.isSelected(); 32 | boolean operationSuccess = exportButton.isSelected() ? enableExporter() : disableExporter(); 33 | exportButton.setSelected(!newSelectedState ^ operationSuccess); 34 | } 35 | }); 36 | 37 | this.add(PanelBuilder.build(new JComponent[][]{ 38 | new JComponent[]{manualSaveButton}, 39 | new JComponent[]{exportButton} 40 | }, new int[][]{ 41 | new int[]{1}, 42 | new int[]{1} 43 | }, Alignment.FILL, 1.0, 1.0), BorderLayout.CENTER); 44 | 45 | this.setBorder(BorderFactory.createTitledBorder("CSV Exporter")); 46 | } 47 | 48 | private boolean enableExporter(){ 49 | try { 50 | this.csvExporter.getExportController().enableExporter(this.csvExporter); 51 | return true; 52 | } catch (Exception e) { 53 | return false; 54 | } 55 | } 56 | 57 | private boolean disableExporter(){ 58 | try{ 59 | this.csvExporter.getExportController().disableExporter(this.csvExporter); 60 | return true; 61 | }catch (Exception e){ 62 | return false; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/ContextMenuExportProvider.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 4 | 5 | import javax.swing.*; 6 | import java.util.List; 7 | 8 | public interface ContextMenuExportProvider { 9 | JMenuItem getExportEntriesMenuItem(List entries); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/ElasticExporterControlPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.util.Globals; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.ActionEvent; 13 | import java.util.concurrent.ExecutionException; 14 | 15 | public class ElasticExporterControlPanel extends JPanel { 16 | 17 | private final ElasticExporter elasticExporter; 18 | private static final String STARTING_TEXT = "Starting Elastic Exporter..."; 19 | private static final String STOPPING_TEXT = "Stopping Elastic Exporter..."; 20 | private static final String START_TEXT = "Start Elastic Exporter"; 21 | private static final String STOP_TEXT = "Stop Elastic Exporter"; 22 | 23 | Logger logger = LogManager.getLogger(this); 24 | 25 | public ElasticExporterControlPanel(ElasticExporter elasticExporter) { 26 | this.elasticExporter = elasticExporter; 27 | this.setLayout(new BorderLayout()); 28 | 29 | JButton showConfigDialogButton = new JButton(new AbstractAction("Configure Elastic Exporter") { 30 | @Override 31 | public void actionPerformed(ActionEvent actionEvent) { 32 | new ElasticExporterConfigDialog(LoggerPlusPlus.instance.getLoggerFrame(), elasticExporter) 33 | .setVisible(true); 34 | 35 | //Dialog closed. Update previous project entry filter to current value. 36 | String newFilter = elasticExporter.getPreferences().getSetting(Globals.PREF_ELASTIC_FILTER); 37 | elasticExporter.getPreferences().setSetting(Globals.PREF_ELASTIC_FILTER_PROJECT_PREVIOUS, newFilter); 38 | } 39 | }); 40 | 41 | JToggleButton exportButton = new JToggleButton("Start Elastic Exporter"); 42 | exportButton.addActionListener(new AbstractAction() { 43 | @Override 44 | public void actionPerformed(ActionEvent actionEvent) { 45 | boolean buttonNowActive = exportButton.isSelected(); 46 | exportButton.setEnabled(false); 47 | exportButton.setText(buttonNowActive ? STARTING_TEXT : STOPPING_TEXT); 48 | new SwingWorker(){ 49 | Exception exception; 50 | 51 | @Override 52 | protected Boolean doInBackground() throws Exception { 53 | boolean success = false; 54 | try { 55 | if (exportButton.isSelected()) { 56 | enableExporter(); 57 | } else { 58 | disableExporter(); 59 | } 60 | success = true; 61 | }catch (Exception e){ 62 | this.exception = e; 63 | } 64 | return success; 65 | } 66 | 67 | @Override 68 | protected void done() { 69 | try { 70 | if(exception != null) { 71 | JOptionPane.showMessageDialog(exportButton, "Could not start elastic exporter: " + 72 | exception.getMessage() + "\nSee the logs for more information.", "Elastic Exporter", JOptionPane.ERROR_MESSAGE); 73 | logger.error("Could not start elastic exporter.", exception); 74 | } 75 | Boolean success = get(); 76 | boolean isRunning = buttonNowActive ^ !success; 77 | exportButton.setSelected(isRunning); 78 | showConfigDialogButton.setEnabled(!isRunning); 79 | 80 | exportButton.setText(isRunning ? STOP_TEXT : START_TEXT); 81 | 82 | } catch (InterruptedException | ExecutionException e) { 83 | e.printStackTrace(); 84 | } 85 | exportButton.setEnabled(true); 86 | } 87 | }.execute(); 88 | } 89 | }); 90 | 91 | if (isExporterEnabled()){ 92 | exportButton.setSelected(true); 93 | exportButton.setText(STOP_TEXT); 94 | showConfigDialogButton.setEnabled(false); 95 | } 96 | 97 | 98 | this.add(PanelBuilder.build(new JComponent[][]{ 99 | new JComponent[]{showConfigDialogButton}, 100 | new JComponent[]{exportButton} 101 | }, new int[][]{ 102 | new int[]{1}, 103 | new int[]{1} 104 | }, Alignment.FILL, 1.0, 1.0), BorderLayout.CENTER); 105 | 106 | 107 | this.setBorder(BorderFactory.createTitledBorder("Elastic Exporter")); 108 | } 109 | 110 | private void enableExporter() throws Exception { 111 | this.elasticExporter.getExportController().enableExporter(this.elasticExporter); 112 | } 113 | 114 | private void disableExporter() throws Exception { 115 | this.elasticExporter.getExportController().disableExporter(this.elasticExporter); 116 | } 117 | 118 | private boolean isExporterEnabled() { 119 | return this.elasticExporter.getExportController().getEnabledExporters().contains(this.elasticExporter); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/ExportController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | 11 | public class ExportController { 12 | private final Preferences preferences; 13 | private final HashMap, LogExporter> exporters; 14 | private final List enabledExporters; 15 | 16 | public ExportController(Preferences preferences) { 17 | this.preferences = preferences; 18 | 19 | this.exporters = new HashMap<>(); 20 | this.enabledExporters = Collections.synchronizedList(new ArrayList<>()); 21 | 22 | initializeExporters(); 23 | } 24 | 25 | private void initializeExporters() { 26 | this.exporters.put(CSVExporter.class, new CSVExporter(this, preferences)); 27 | this.exporters.put(JSONExporter.class, new JSONExporter(this, preferences)); 28 | this.exporters.put(HARExporter.class, new HARExporter(this, preferences)); 29 | this.exporters.put(Base64Exporter.class, new Base64Exporter(this, preferences)); 30 | this.exporters.put(ElasticExporter.class, new ElasticExporter(this, preferences)); 31 | } 32 | 33 | public HashMap, LogExporter> getExporters() { 34 | return exporters; 35 | } 36 | 37 | public List getEnabledExporters() { 38 | return enabledExporters; 39 | } 40 | 41 | public void enableExporter(AutomaticLogExporter logExporter) throws Exception { 42 | logExporter.setup(); 43 | this.enabledExporters.add(logExporter); 44 | } 45 | 46 | public void disableExporter(AutomaticLogExporter logExporter) throws Exception { 47 | this.enabledExporters.remove(logExporter); 48 | logExporter.shutdown(); 49 | } 50 | 51 | public void exportNewEntry(LogEntry logEntry) { 52 | for (AutomaticLogExporter exporter : this.enabledExporters) { 53 | exporter.exportNewEntry(logEntry); 54 | } 55 | } 56 | 57 | public void exportUpdatedEntry(LogEntry logEntry) { 58 | for (AutomaticLogExporter exporter : this.enabledExporters) { 59 | exporter.exportUpdatedEntry(logEntry); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/ExportPanelProvider.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import javax.swing.*; 4 | 5 | public interface ExportPanelProvider { 6 | /** 7 | * Build the control panel to be displayed in the preferences tab 8 | * 9 | * @return JComponent Component to be displayed 10 | */ 11 | JComponent getExportPanel(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/HARExporter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.google.gson.reflect.TypeToken; 7 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 8 | import com.nccgroup.loggerplusplus.util.Globals; 9 | import com.nccgroup.loggerplusplus.util.MoreHelp; 10 | import com.nccgroup.loggerplusplus.util.SwingWorkerWithProgressDialog; 11 | 12 | import javax.swing.*; 13 | import java.awt.event.ActionEvent; 14 | import java.io.File; 15 | import java.io.FileWriter; 16 | import java.lang.reflect.Type; 17 | import java.util.List; 18 | 19 | public class HARExporter extends LogExporter implements ExportPanelProvider, ContextMenuExportProvider { 20 | 21 | private final HARExporterControlPanel controlPanel; 22 | 23 | public HARExporter(ExportController exportController, Preferences preferences) { 24 | super(exportController, preferences); 25 | this.controlPanel = new HARExporterControlPanel(this); 26 | } 27 | 28 | @Override 29 | public JComponent getExportPanel() { 30 | return this.controlPanel; 31 | } 32 | 33 | public void exportEntries(List entries) { 34 | try { 35 | File file = MoreHelp.getSaveFile("LoggerPlusPlus.har", "HAR Format", "har"); 36 | if (file.exists() && !MoreHelp.shouldOverwriteExistingFilePrompt()) 37 | return; 38 | 39 | SwingWorkerWithProgressDialog importWorker = new SwingWorkerWithProgressDialog( 40 | JOptionPane.getFrameForComponent(this.controlPanel), "HAR Export", "Exporting as HAR...", 41 | entries.size()) { 42 | @Override 43 | protected Void doInBackground() throws Exception { 44 | super.doInBackground(); 45 | try (FileWriter fileWriter = new FileWriter(file, false)) { 46 | Type logEntryListType = new TypeToken>(){}.getType(); 47 | Gson gson = new GsonBuilder().registerTypeAdapter(logEntryListType, new HarSerializer(String.valueOf(Globals.VERSION), "LoggerPlusPlus")).create(); 48 | gson.toJson(entries, logEntryListType, fileWriter); 49 | } 50 | 51 | return null; 52 | } 53 | 54 | @Override 55 | protected void done() { 56 | super.done(); 57 | JOptionPane.showMessageDialog(controlPanel, "Export as HAR completed.", "HAR Export", 58 | JOptionPane.INFORMATION_MESSAGE); 59 | } 60 | }; 61 | 62 | importWorker.execute(); 63 | 64 | } catch (Exception e) { 65 | // Cancelled. 66 | } 67 | } 68 | 69 | @Override 70 | public JMenuItem getExportEntriesMenuItem(List entries) { 71 | return new JMenuItem(new AbstractAction( 72 | String.format("Export %d %s as HAR", entries.size(), entries.size() != 1 ? "entries" : "entry")) { 73 | @Override 74 | public void actionPerformed(ActionEvent e) { 75 | exportEntries(entries); 76 | } 77 | }); 78 | } 79 | 80 | public ExportController getExportController() { 81 | return this.exportController; 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/HARExporterControlPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.util.List; 11 | 12 | public class HARExporterControlPanel extends JPanel { 13 | HARExporterControlPanel(HARExporter harExporter) { 14 | this.setLayout(new BorderLayout()); 15 | 16 | JButton manualSaveButton = new JButton("Export as HAR"); 17 | manualSaveButton.addActionListener(actionEvent -> { 18 | final List entries = LoggerPlusPlus.instance.getLogEntries(); 19 | harExporter.exportEntries(entries); 20 | }); 21 | 22 | this.add(PanelBuilder.build(new JComponent[][] { new JComponent[] { manualSaveButton }, }, 23 | new int[][] { new int[] { 1 }, }, Alignment.FILL, 1.0, 1.0), BorderLayout.CENTER); 24 | 25 | this.setBorder(BorderFactory.createTitledBorder("HAR Exporter")); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/JSONExporter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.google.gson.Gson; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 7 | import com.nccgroup.loggerplusplus.util.MoreHelp; 8 | import com.nccgroup.loggerplusplus.util.SwingWorkerWithProgressDialog; 9 | 10 | import javax.swing.*; 11 | import java.awt.event.ActionEvent; 12 | import java.io.File; 13 | import java.io.FileWriter; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by corey on 21/08/17. 18 | */ 19 | public class JSONExporter extends LogExporter implements ExportPanelProvider, ContextMenuExportProvider { 20 | 21 | private final JSONExporterControlPanel controlPanel; 22 | 23 | public JSONExporter(ExportController exportController, Preferences preferences) { 24 | super(exportController, preferences); 25 | this.controlPanel = new JSONExporterControlPanel(this); 26 | } 27 | 28 | @Override 29 | public JComponent getExportPanel() { 30 | return this.controlPanel; 31 | } 32 | 33 | public void exportEntries(List entries) { 34 | try { 35 | File file = MoreHelp.getSaveFile("LoggerPlusPlus.json", "JSON Format", "json"); 36 | if (file.exists() && !MoreHelp.shouldOverwriteExistingFilePrompt()) return; 37 | 38 | SwingWorkerWithProgressDialog importWorker = new SwingWorkerWithProgressDialog( 39 | JOptionPane.getFrameForComponent(this.controlPanel), 40 | "JSON Export", "Exporting as JSON...", entries.size()){ 41 | @Override 42 | protected Void doInBackground() throws Exception { 43 | super.doInBackground(); 44 | try(FileWriter fileWriter = new FileWriter(file, false)) { 45 | Gson gson = LoggerPlusPlus.gsonProvider.getGson(); 46 | gson.toJson(entries, fileWriter); 47 | } 48 | 49 | return null; 50 | } 51 | 52 | @Override 53 | protected void done() { 54 | super.done(); 55 | JOptionPane.showMessageDialog(controlPanel, "Export as JSON completed.", 56 | "JSON Export", JOptionPane.INFORMATION_MESSAGE); 57 | } 58 | }; 59 | 60 | importWorker.execute(); 61 | 62 | }catch (Exception e){ 63 | //Cancelled. 64 | } 65 | } 66 | 67 | @Override 68 | public JMenuItem getExportEntriesMenuItem(List entries) { 69 | return new JMenuItem(new AbstractAction(String.format("Export %d %s as JSON", 70 | entries.size(), entries.size() != 1 ? "entries" : "entry")) { 71 | @Override 72 | public void actionPerformed(ActionEvent e) { 73 | exportEntries(entries); 74 | } 75 | }); 76 | } 77 | 78 | public ExportController getExportController() { 79 | return this.exportController; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/JSONExporterControlPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.util.List; 11 | 12 | public class JSONExporterControlPanel extends JPanel { 13 | 14 | JSONExporterControlPanel(JSONExporter jsonExporter){ 15 | this.setLayout(new BorderLayout()); 16 | 17 | JButton manualSaveButton = new JButton("Export as JSON"); 18 | manualSaveButton.addActionListener(actionEvent -> { 19 | final List entries = LoggerPlusPlus.instance.getLogEntries(); 20 | jsonExporter.exportEntries(entries); 21 | }); 22 | 23 | this.add(PanelBuilder.build(new JComponent[][]{ 24 | new JComponent[]{manualSaveButton}, 25 | }, new int[][]{ 26 | new int[]{1}, 27 | }, Alignment.FILL, 1.0, 1.0), BorderLayout.CENTER); 28 | 29 | this.setBorder(BorderFactory.createTitledBorder("JSON Exporter")); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/exports/LogExporter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.exports; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | public abstract class LogExporter { 6 | 7 | protected final ExportController exportController; 8 | protected final Preferences preferences; 9 | 10 | protected LogExporter(ExportController exportController, Preferences preferences){ 11 | this.exportController = exportController; 12 | this.preferences = preferences; 13 | } 14 | 15 | public Preferences getPreferences() { 16 | return preferences; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/ColorizingFilterRule.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.annotations.JsonAdapter; 4 | import com.nccgroup.loggerplusplus.filter.colorfilter.TableColorRule; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.awt.*; 9 | 10 | public abstract class ColorizingFilterRule extends FilterRule { 11 | 12 | @Getter @Setter 13 | private Color backgroundColor; 14 | @Getter @Setter 15 | private Color foregroundColor; 16 | @Getter @Setter 17 | private short priority; 18 | @Getter @Setter 19 | private boolean enabled; 20 | 21 | protected ColorizingFilterRule(String name){ 22 | super(name); 23 | } 24 | 25 | protected ColorizingFilterRule(String name, FilterExpression filterExpression){ 26 | super(name, filterExpression); 27 | } 28 | 29 | protected ColorizingFilterRule(String name, String filter){ 30 | super(name, filter); 31 | } 32 | 33 | @Override 34 | public boolean equals(Object obj) { 35 | if(obj instanceof ColorizingFilterRule){ 36 | return ((ColorizingFilterRule) obj).getUuid().equals(this.getUuid()); 37 | }else{ 38 | return super.equals(obj); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/ComparisonOperator.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | public enum ComparisonOperator { 4 | EQUAL("=="), NOT_EQUAL("!="), GREATER_THAN(">"), LESS_THAN("<"), 5 | GREATER_THAN_EQUAL(">="), LESS_THAN_EQUAL("<="), CONTAINS("CONTAINS"), IN("IN"), MATCHES("MATCHES"); 6 | 7 | private final String label; 8 | 9 | ComparisonOperator(String label) { 10 | this.label = label; 11 | } 12 | 13 | public String getLabel() { 14 | return this.label; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return label; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/FilterExpression.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 4 | import com.nccgroup.loggerplusplus.filter.parser.*; 5 | import com.nccgroup.loggerplusplus.logentry.FieldGroup; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 7 | import com.nccgroup.loggerplusplus.logentry.LogEntryField; 8 | import lombok.Getter; 9 | 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | 13 | public class FilterExpression { 14 | 15 | @Getter 16 | protected ASTExpression ast; 17 | 18 | @Getter 19 | protected HashSet snippetDependencies; 20 | 21 | @Getter 22 | protected HashSet requiredContexts; 23 | 24 | public FilterExpression(String filterString) throws ParseException { 25 | this(null, filterString); 26 | } 27 | 28 | public FilterExpression(String alias, String filterString) throws ParseException { 29 | this.ast = FilterParser.parseFilter(filterString); 30 | HashMap filterInfo = FilterParser.validateFilterDependencies(LoggerPlusPlus.instance.getLibraryController(), alias, this.ast); 31 | snippetDependencies = (HashSet) filterInfo.get("dependencies"); 32 | requiredContexts = (HashSet) filterInfo.get("contexts"); 33 | } 34 | 35 | public boolean matches(LogEntry entry){ 36 | FilterEvaluationVisitor visitor = new FilterEvaluationVisitor(LoggerPlusPlus.instance.getLibraryController()); 37 | return visitor.visit(ast, entry); 38 | } 39 | 40 | public void addConditionToFilter(LogicalOperator logicalOperator, LogEntryField field, 41 | ComparisonOperator booleanOperator, String value) throws ParseException { 42 | String existing; 43 | if (this.ast.getLogicalOperator() != null && !this.ast.getLogicalOperator().equals(logicalOperator)) { 44 | existing = "(" + this.ast.getFilterString() + ")"; 45 | } else { 46 | existing = this.ast.getFilterString(); 47 | } 48 | 49 | this.ast = FilterParser.parseFilter(String.format("%s %s %s %s %s", existing, logicalOperator.toString(), field.toString(), booleanOperator, value)); 50 | HashMap filterInfo = FilterParser.validateFilterDependencies(LoggerPlusPlus.instance.getLibraryController(), null, this.ast); 51 | snippetDependencies = (HashSet) filterInfo.get("dependencies"); 52 | requiredContexts = (HashSet) filterInfo.get("contexts"); 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return ast.getFilterString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/FilterExpressionSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.*; 4 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | public class FilterExpressionSerializer implements JsonSerializer, JsonDeserializer { 9 | 10 | @Override 11 | public JsonElement serialize(FilterExpression filter, Type type, JsonSerializationContext jsonSerializationContext) { 12 | JsonObject object = new JsonObject(); 13 | object.addProperty("filter", filter.toString()); 14 | return object; 15 | } 16 | 17 | @Override 18 | public FilterExpression deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { 19 | FilterExpression filter = null; 20 | try { 21 | filter = new FilterExpression(jsonElement.getAsJsonObject().get("filter").getAsString()); 22 | } catch (ParseException e) { 23 | } 24 | return filter; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/FilterRule.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 5 | import lombok.Getter; 6 | 7 | import java.util.UUID; 8 | 9 | public abstract class FilterRule { 10 | 11 | @Getter @SerializedName("uid") 12 | private UUID uuid; 13 | @Getter 14 | private String name; 15 | @Getter 16 | private String filterString; 17 | @Getter @SerializedName("filter") 18 | private FilterExpression filterExpression; 19 | 20 | public FilterRule(String name){ 21 | this.name = name; 22 | this.uuid = UUID.randomUUID(); 23 | } 24 | 25 | public FilterRule(String name, FilterExpression filterExpression){ 26 | this(name); 27 | this.filterExpression = filterExpression; 28 | this.filterString = filterExpression.toString(); 29 | } 30 | 31 | public FilterRule(String name, String filterString){ 32 | this(name); 33 | trySetFilter(filterString); 34 | } 35 | 36 | protected void setUuid(UUID uuid) { 37 | this.uuid = uuid; 38 | } 39 | 40 | public void setFilter(FilterExpression expression) { 41 | this.filterExpression = expression; 42 | if(expression != null) this.filterString = expression.toString(); 43 | } 44 | 45 | public void setName(String name){ 46 | this.name = name; 47 | } 48 | 49 | public boolean trySetFilter(String filterString){ 50 | this.filterString = filterString; 51 | try{ 52 | parseAndSetFilter(filterString); 53 | }catch (ParseException e){ 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | public void parseAndSetFilter(String filterString) throws ParseException { 61 | this.filterString = filterString; 62 | try { 63 | this.filterExpression = new FilterExpression(name, filterString); 64 | }catch (ParseException e){ 65 | this.filterExpression = null; 66 | throw e; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/LogicalOperator.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | public enum LogicalOperator { 4 | AND("AND"), OR("OR"), XOR("XOR"); 5 | 6 | private final String label; 7 | 8 | LogicalOperator(String label){ 9 | this.label = label; 10 | } 11 | 12 | public String getLabel() { 13 | return label; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/SavedFilterSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.*; 4 | import com.nccgroup.loggerplusplus.filter.savedfilter.SavedFilter; 5 | 6 | import java.lang.reflect.Type; 7 | import java.util.UUID; 8 | 9 | public class SavedFilterSerializer implements JsonSerializer, JsonDeserializer { 10 | 11 | @Override 12 | public JsonElement serialize(SavedFilter tag, Type type, JsonSerializationContext context) { 13 | JsonObject object = new JsonObject(); 14 | object.addProperty("uid", tag.getUuid().toString()); 15 | object.addProperty("name", tag.getName()); 16 | object.add("filter", context.serialize(tag.getFilterExpression())); 17 | return object; 18 | } 19 | 20 | @Override 21 | public SavedFilter deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { 22 | JsonObject obj = jsonElement.getAsJsonObject(); 23 | String uid = obj.get("uid").getAsString(); 24 | String name = obj.get("name").getAsString(); 25 | FilterExpression filterExpression = context.deserialize(obj.get("filter"), FilterExpression.class); 26 | 27 | SavedFilter tag = new SavedFilter(name, filterExpression); 28 | tag.setUuid(UUID.fromString(uid)); 29 | return tag; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/TableColorRuleSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.*; 4 | import com.nccgroup.loggerplusplus.filter.colorfilter.TableColorRule; 5 | 6 | import java.awt.*; 7 | import java.lang.reflect.Type; 8 | import java.util.UUID; 9 | 10 | public class TableColorRuleSerializer implements JsonSerializer, JsonDeserializer { 11 | 12 | @Override 13 | public JsonElement serialize(TableColorRule tag, Type type, JsonSerializationContext context) { 14 | JsonObject object = new JsonObject(); 15 | object.addProperty("uid", tag.getUuid().toString()); 16 | object.addProperty("name", tag.getName()); 17 | object.add("filter", context.serialize(tag.getFilterExpression())); 18 | object.add("foregroundColor", context.serialize(tag.getForegroundColor())); 19 | object.add("backgroundColor", context.serialize(tag.getBackgroundColor())); 20 | object.addProperty("enabled", tag.isEnabled()); 21 | object.addProperty("priority", tag.getPriority()); 22 | return object; 23 | } 24 | 25 | @Override 26 | public TableColorRule deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { 27 | JsonObject obj = jsonElement.getAsJsonObject(); 28 | String uid = obj.get("uid").getAsString(); 29 | String name = obj.has("name") ? obj.get("name").getAsString() : ""; 30 | FilterExpression filterExpression = context.deserialize(obj.get("filter"), FilterExpression.class); 31 | Color foregroundColor = context.deserialize(obj.get("foregroundColor"), Color.class); 32 | Color backgroundColor = context.deserialize(obj.get("backgroundColor"), Color.class); 33 | boolean enabled = obj.get("enabled").getAsBoolean(); 34 | short priority = obj.get("priority").getAsShort(); 35 | 36 | TableColorRule tag = new TableColorRule(name, filterExpression); 37 | tag.setUuid(UUID.fromString(uid)); 38 | tag.setForegroundColor(foregroundColor); 39 | tag.setBackgroundColor(backgroundColor); 40 | tag.setEnabled(enabled); 41 | tag.setPriority(priority); 42 | return tag; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/TagSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter; 2 | 3 | import com.google.gson.*; 4 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 5 | import com.nccgroup.loggerplusplus.filter.tag.Tag; 6 | 7 | import java.awt.*; 8 | import java.lang.reflect.Type; 9 | import java.util.UUID; 10 | 11 | public class TagSerializer implements JsonSerializer, JsonDeserializer { 12 | 13 | @Override 14 | public JsonElement serialize(Tag tag, Type type, JsonSerializationContext context) { 15 | JsonObject object = new JsonObject(); 16 | object.addProperty("uid", tag.getUuid().toString()); 17 | object.addProperty("name", tag.getName()); 18 | object.add("filter", context.serialize(tag.getFilterExpression())); 19 | object.add("foregroundColor", context.serialize(tag.getForegroundColor())); 20 | object.add("backgroundColor", context.serialize(tag.getBackgroundColor())); 21 | object.addProperty("enabled", tag.isEnabled()); 22 | object.addProperty("priority", tag.getPriority()); 23 | return object; 24 | } 25 | 26 | @Override 27 | public Tag deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { 28 | JsonObject obj = jsonElement.getAsJsonObject(); 29 | String uid = obj.get("uid").getAsString(); 30 | String name = obj.get("name").getAsString(); 31 | FilterExpression filterExpression = context.deserialize(obj.get("filter"), FilterExpression.class); 32 | Color foregroundColor = context.deserialize(obj.get("foregroundColor"), Color.class); 33 | Color backgroundColor = context.deserialize(obj.get("backgroundColor"), Color.class); 34 | boolean enabled = obj.get("enabled").getAsBoolean(); 35 | short priority = obj.get("priority").getAsShort(); 36 | 37 | Tag tag = new Tag(name, filterExpression); 38 | tag.setUuid(UUID.fromString(uid)); 39 | tag.setForegroundColor(foregroundColor); 40 | tag.setBackgroundColor(backgroundColor); 41 | tag.setEnabled(enabled); 42 | tag.setPriority(priority); 43 | return tag; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/colorfilter/ColorFilterListener.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.colorfilter; 2 | 3 | /** 4 | * Created by corey on 20/07/17. 5 | */ 6 | public interface ColorFilterListener { 7 | 8 | void onColorFilterChange(TableColorRule filter); 9 | 10 | void onColorFilterAdd(TableColorRule filter); 11 | 12 | void onColorFilterRemove(TableColorRule filter); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/colorfilter/TableColorRule.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.colorfilter; 2 | 3 | import com.nccgroup.loggerplusplus.filter.ColorizingFilterRule; 4 | import com.nccgroup.loggerplusplus.filter.FilterExpression; 5 | import lombok.AccessLevel; 6 | import lombok.Getter; 7 | 8 | /** 9 | * Created by corey on 19/07/17. 10 | */ 11 | public class TableColorRule extends ColorizingFilterRule implements Comparable{ 12 | 13 | @Getter(AccessLevel.PUBLIC) 14 | private boolean shouldRetest = true; 15 | 16 | public TableColorRule(){ 17 | super("", ""); 18 | } 19 | 20 | public TableColorRule(String title, String filterString) { 21 | super(title, filterString); 22 | } 23 | 24 | public TableColorRule(String title, FilterExpression filterExpression) { 25 | super(title, filterExpression); 26 | } 27 | 28 | @Override 29 | public boolean trySetFilter(String filterString){ 30 | boolean success = super.trySetFilter(filterString); 31 | if(success) shouldRetest = true; 32 | return success; 33 | } 34 | 35 | @Override 36 | public void setFilter(FilterExpression filter) { 37 | super.setFilter(filter); 38 | shouldRetest = true; 39 | } 40 | 41 | @Override 42 | public void setEnabled(boolean enabled) { 43 | super.setEnabled(enabled); 44 | shouldRetest = true; 45 | } 46 | 47 | @Override 48 | public int compareTo(TableColorRule tableColorRule) { 49 | return ((Comparable) this.getPriority()).compareTo(tableColorRule.getPriority()); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "ColorFilter[" + (this.getFilterExpression() != null ? this.getFilterExpression().toString() : getFilterString()) + "]"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/logfilter/LogTableFilter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.logfilter; 2 | 3 | import com.nccgroup.loggerplusplus.filter.FilterExpression; 4 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 5 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 6 | import com.nccgroup.loggerplusplus.logview.logtable.LogTableModel; 7 | import lombok.Getter; 8 | 9 | import javax.swing.*; 10 | import javax.swing.table.TableModel; 11 | 12 | public class LogTableFilter extends RowFilter { 13 | 14 | @Getter 15 | private FilterExpression filterExpression; 16 | 17 | public LogTableFilter(String filterString) throws ParseException { 18 | this.filterExpression = new FilterExpression(filterString); 19 | } 20 | 21 | public LogTableFilter(FilterExpression filterExpression){ 22 | this.filterExpression = filterExpression; 23 | } 24 | 25 | @Override 26 | public boolean include(RowFilter.Entry entry) { 27 | int index = (int) entry.getIdentifier(); 28 | TableModel tableModel = (TableModel) entry.getModel(); 29 | if(tableModel instanceof LogTableModel){ 30 | LogEntry logEntry = ((LogTableModel) tableModel).getRow(index); 31 | return filterExpression.matches(logEntry); 32 | } 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/ASTAlias.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree: Do not edit this line. ASTAlias.java Version 7.0 */ 2 | /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | public 6 | class ASTAlias extends SimpleNode { 7 | 8 | public String identifier; 9 | public ASTExpression filter; 10 | 11 | public ASTAlias(int id) { 12 | super(id); 13 | } 14 | 15 | public ASTAlias(FilterParser p, int id) { 16 | super(p, id); 17 | } 18 | 19 | @Override 20 | public String getFilterString() { 21 | return "#" + identifier; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return String.format("ASTAlias[id=%s]", identifier); 27 | } 28 | 29 | /** Accept the visitor. **/ 30 | public Object jjtAccept(FilterParserVisitor visitor, VisitorData data) { 31 | 32 | return 33 | visitor.visit(this, data); 34 | } 35 | } 36 | /* JavaCC - OriginalChecksum=b5d9705c3b2352700f09763675b026ae (do not edit this line) */ 37 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/ASTComparison.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree: Do not edit this line. ASTComparison.java Version 7.0 */ 2 | /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | import com.nccgroup.loggerplusplus.filter.ComparisonOperator; 6 | import com.nccgroup.loggerplusplus.logentry.LogEntryField; 7 | import com.nccgroup.loggerplusplus.logview.processor.LogProcessor; 8 | import org.apache.commons.text.StringEscapeUtils; 9 | 10 | import java.util.Date; 11 | import java.util.Set; 12 | import java.util.regex.Pattern; 13 | import java.util.stream.Collectors; 14 | 15 | public 16 | class ASTComparison extends SimpleNode { 17 | 18 | Object left, right; 19 | ComparisonOperator comparisonOperator; 20 | 21 | public ASTComparison(int id) { 22 | super(id); 23 | } 24 | 25 | public ASTComparison(FilterParser p, int id) { 26 | super(p, id); 27 | } 28 | 29 | /** 30 | * Accept the visitor. 31 | **/ 32 | public Object jjtAccept(FilterParserVisitor visitor, VisitorData data) { 33 | 34 | return visitor.visit(this, data); 35 | } 36 | 37 | public ComparisonOperator getComparisonOperator() { 38 | return comparisonOperator; 39 | } 40 | 41 | public Object getLeft() { 42 | return left; 43 | } 44 | 45 | public Object getRight() { 46 | return right; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | Class leftClass = left instanceof LogEntryField ? ((LogEntryField) left).getType() : left.getClass(); 52 | Class rightClass = right instanceof LogEntryField ? ((LogEntryField) right).getType() : right.getClass(); 53 | return String.format("ASTComparison[left=%s (%s), op=%s, right=%s (%s)]", left, leftClass, comparisonOperator, right, rightClass); 54 | } 55 | 56 | @Override 57 | public String getFilterString() { 58 | return String.format("%s %s %s", convertObjectToString(left), comparisonOperator.getLabel(), convertObjectToString(right)); 59 | } 60 | 61 | private String convertObjectToString(Object obj){ 62 | if(obj instanceof Pattern) { 63 | if (comparisonOperator == ComparisonOperator.MATCHES) return "\"" + obj + "\""; 64 | else return "/" + obj + "/"; 65 | }else if(obj instanceof String){ 66 | return "\"" + StringEscapeUtils.escapeJava((String) obj) + "\""; 67 | }else if(obj instanceof Set){ 68 | StringBuilder sb = new StringBuilder(); 69 | sb.append("["); 70 | sb.append(((Set) obj).stream().map(item -> { 71 | if(item instanceof String) return "\"" + item + "\""; 72 | else return String.valueOf(item); 73 | }).collect(Collectors.joining(", "))); 74 | sb.append("]"); 75 | return sb.toString(); 76 | }else if(obj instanceof Date){ 77 | return "\"" + LogProcessor.LOGGER_DATE_FORMAT.format(obj) + "\""; 78 | } 79 | return String.valueOf(obj); 80 | } 81 | 82 | } 83 | /* JavaCC - OriginalChecksum=bdaeeba5ee349a11518169d312e524e0 (do not edit this line) */ 84 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/ASTExpression.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree: Do not edit this line. ASTExpression.java Version 7.0 */ 2 | /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | import com.nccgroup.loggerplusplus.filter.LogicalOperator; 6 | 7 | public 8 | class ASTExpression extends SimpleNode { 9 | 10 | boolean inverse = false; 11 | LogicalOperator op; 12 | 13 | public ASTExpression(int id) { 14 | super(id); 15 | } 16 | 17 | public ASTExpression(FilterParser p, int id) { 18 | super(p, id); 19 | } 20 | 21 | @Override 22 | public String getFilterString() { 23 | StringBuilder sb = new StringBuilder(); 24 | for (int i = 0; i < this.children.length; i++) { 25 | String childString = this.children[i].getFilterString(); 26 | if (this.children[i] instanceof ASTExpression && !((ASTExpression) this.children[i]).inverse) 27 | sb.append("(" + childString + ")"); 28 | else 29 | sb.append(childString); 30 | 31 | if (i != this.children.length - 1) { 32 | sb.append(" " + op.getLabel() + " "); 33 | } 34 | } 35 | 36 | if (inverse) return "!(" + sb.toString() + ")"; 37 | else return sb.toString(); 38 | } 39 | 40 | public LogicalOperator getLogicalOperator() { 41 | return op; 42 | } 43 | 44 | public void addCondition(ASTExpression comparison){ 45 | jjtAddChild(comparison, this.jjtGetNumChildren()); 46 | } 47 | 48 | public void addCondition(ASTComparison comparison){ 49 | jjtAddChild(comparison, this.jjtGetNumChildren()); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return String.format("ASTExpression[inverse=%s, op=%s]", inverse, op); 55 | } 56 | 57 | /** 58 | * Accept the visitor. 59 | **/ 60 | public Object jjtAccept(FilterParserVisitor visitor, VisitorData data) { 61 | return visitor.visit(this, data); 62 | } 63 | } 64 | /* JavaCC - OriginalChecksum=f3e2fc4905864ee71fff41d667f32963 (do not edit this line) */ 65 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/AliasCheckVisitor.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. FilterParserDefaultVisitor.java Version 7.0.2 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | import com.nccgroup.loggerplusplus.filter.savedfilter.SavedFilter; 5 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 6 | import com.nccgroup.loggerplusplus.logentry.FieldGroup; 7 | import com.nccgroup.loggerplusplus.logentry.LogEntryField; 8 | import lombok.extern.log4j.Log4j2; 9 | 10 | import java.util.*; 11 | import java.util.stream.Collectors; 12 | 13 | @Log4j2 14 | public class AliasCheckVisitor implements FilterParserVisitor{ 15 | 16 | private FilterLibraryController filterLibraryController; 17 | 18 | public AliasCheckVisitor(FilterLibraryController filterLibraryController){ 19 | this.filterLibraryController = filterLibraryController; 20 | } 21 | 22 | public VisitorData defaultVisit(SimpleNode node, VisitorData data){ 23 | node.childrenAccept(this, data); 24 | return data; 25 | } 26 | 27 | @Override 28 | public VisitorData visit(SimpleNode node, VisitorData data){ 29 | return defaultVisit(node, data); 30 | } 31 | 32 | public VisitorData visit(String alias, SimpleNode node){ 33 | log.debug("Starting sanity check on expression (%s) alias (%s) ".formatted(node.toString(), alias)); 34 | VisitorData visitorData = new VisitorData(); 35 | visitorData.setData("dependencies", new HashSet()); 36 | visitorData.setData("contexts", new HashSet()); 37 | Stack visitStack = new Stack(); 38 | visitorData.setData("aliasVisitList", visitStack); 39 | if (alias != null) { 40 | visitStack.push(alias.toUpperCase()); 41 | } 42 | return visit(node, visitorData); 43 | } 44 | 45 | @Override 46 | public VisitorData visit(ASTExpression node, VisitorData data){ 47 | return defaultVisit(node, data); 48 | } 49 | 50 | @Override 51 | public VisitorData visit(ASTComparison node, VisitorData visitorData){ 52 | HashSet contexts = (HashSet) visitorData.getData().get("contexts"); 53 | if(node.left instanceof LogEntryField) contexts.add(((LogEntryField) node.left).getFieldGroup()); 54 | if(node.right instanceof LogEntryField) contexts.add(((LogEntryField) node.right).getFieldGroup()); 55 | defaultVisit(node, visitorData); 56 | return visitorData; 57 | } 58 | 59 | @Override 60 | public VisitorData visit(ASTAlias node, VisitorData data) { 61 | //Add this alias to our dependencies 62 | Stack aliasVisitList = (Stack) data.getData().get("aliasVisitList"); 63 | try { 64 | log.debug("Visiting " + node.identifier); 65 | if (aliasVisitList.contains(node.identifier.toUpperCase())) { 66 | //We're recursing, don't continue! 67 | aliasVisitList.push(node.identifier.toUpperCase()); 68 | String visitOrder = aliasVisitList.stream().collect(Collectors.joining("->")); 69 | data.addError("Recursion detected in filter. Alias trace: " + visitOrder); 70 | return data; 71 | } 72 | 73 | aliasVisitList.push(node.identifier.toUpperCase()); 74 | log.debug("Current Visit Queue " + aliasVisitList.toString()); 75 | 76 | 77 | ((HashSet) data.getData().get("dependencies")).add(node.identifier.toUpperCase()); 78 | 79 | //Now sanity check on the aliased filter with our existing data 80 | Optional aliasedFilter = filterLibraryController.getFilterSnippets().stream().filter(savedFilter -> savedFilter.getName().equalsIgnoreCase(node.identifier)).findFirst(); 81 | if (aliasedFilter.isPresent()) { 82 | visit(aliasedFilter.get().getFilterExpression().getAst(), data); 83 | } else { 84 | data.addError("Could not find a filter in the library for alias: " + node.identifier); 85 | } 86 | 87 | 88 | return data; 89 | }finally { 90 | log.debug("Leaving " + node.identifier); 91 | aliasVisitList.pop(); 92 | log.debug("Current Visit Queue " + aliasVisitList.toString()); 93 | } 94 | } 95 | } 96 | /* JavaCC - OriginalChecksum=b30458d637879c9662107beea18204f0 (do not edit this line) */ 97 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/FilterParserConstants.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree&JavaCC: Do not edit this line. FilterParserConstants.java */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | 5 | /** 6 | * Token literal values and constants. 7 | * Generated by org.javacc.parser.OtherFilesGen#start() 8 | */ 9 | public interface FilterParserConstants { 10 | 11 | /** End of File. */ 12 | int EOF = 0; 13 | /** RegularExpression Id. */ 14 | int EQ = 2; 15 | /** RegularExpression Id. */ 16 | int NEQ = 3; 17 | /** RegularExpression Id. */ 18 | int GT = 4; 19 | /** RegularExpression Id. */ 20 | int LT = 5; 21 | /** RegularExpression Id. */ 22 | int GEQ = 6; 23 | /** RegularExpression Id. */ 24 | int LEQ = 7; 25 | /** RegularExpression Id. */ 26 | int OR = 8; 27 | /** RegularExpression Id. */ 28 | int AND = 9; 29 | /** RegularExpression Id. */ 30 | int XOR = 10; 31 | /** RegularExpression Id. */ 32 | int CONTAINS = 11; 33 | /** RegularExpression Id. */ 34 | int IN = 12; 35 | /** RegularExpression Id. */ 36 | int BOOLEAN = 13; 37 | /** RegularExpression Id. */ 38 | int NUMBER = 14; 39 | /** RegularExpression Id. */ 40 | int DIGIT = 15; 41 | /** RegularExpression Id. */ 42 | int REGEXLITERAL_IN_FORWARD_SLASHES = 16; 43 | /** RegularExpression Id. */ 44 | int REGEX_IN_FORWARD_SLASHES = 17; 45 | /** RegularExpression Id. */ 46 | int MATCHES = 18; 47 | /** RegularExpression Id. */ 48 | int LPAREN = 19; 49 | /** RegularExpression Id. */ 50 | int RPAREN = 20; 51 | /** RegularExpression Id. */ 52 | int INVERSE = 21; 53 | /** RegularExpression Id. */ 54 | int DOT = 22; 55 | /** RegularExpression Id. */ 56 | int IDENTIFIER = 23; 57 | /** RegularExpression Id. */ 58 | int ARRAY_START = 24; 59 | /** RegularExpression Id. */ 60 | int ARRAY_END = 25; 61 | /** RegularExpression Id. */ 62 | int ARRAY_SEPARATOR = 26; 63 | /** RegularExpression Id. */ 64 | int ALIAS_SYMBOL = 27; 65 | /** RegularExpression Id. */ 66 | int OPEN_SINGLE_QUOTE_STRING = 28; 67 | /** RegularExpression Id. */ 68 | int OPEN_DOUBLE_QUOTE_STRING = 29; 69 | /** RegularExpression Id. */ 70 | int SINGLE_STRING_BODY = 30; 71 | /** RegularExpression Id. */ 72 | int CLOSE_SINGLE_QUOTE_STRING = 31; 73 | /** RegularExpression Id. */ 74 | int DOUBLE_STRING_BODY = 32; 75 | /** RegularExpression Id. */ 76 | int CLOSE_DOUBLE_QUOTE_STRING = 33; 77 | /** RegularExpression Id. */ 78 | int UNKNOWN = 34; 79 | 80 | /** Lexical state. */ 81 | int DEFAULT = 0; 82 | /** Lexical state. */ 83 | int SINGLE_QUOTED_STRING = 1; 84 | /** Lexical state. */ 85 | int DOUBLE_QUOTED_STRING = 2; 86 | 87 | /** Literal token values. */ 88 | String[] tokenImage = { 89 | "", 90 | "\" \"", 91 | "", 92 | "\"!=\"", 93 | "\">\"", 94 | "\"<\"", 95 | "\">=\"", 96 | "\"<=\"", 97 | "", 98 | "", 99 | "", 100 | "\"CONTAINS\"", 101 | "\"IN\"", 102 | "", 103 | "", 104 | "", 105 | "", 106 | "", 107 | "\"MATCHES\"", 108 | "\"(\"", 109 | "\")\"", 110 | "", 111 | "\".\"", 112 | "", 113 | "\"[\"", 114 | "\"]\"", 115 | "\",\"", 116 | "\"#\"", 117 | "\"\\\'\"", 118 | "\"\\\"\"", 119 | "", 120 | "\"\\\'\"", 121 | "", 122 | "\"\\\"\"", 123 | "", 124 | }; 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/FilterParserDefaultVisitor.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. FilterParserDefaultVisitor.java Version 7.0.2 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | public class FilterParserDefaultVisitor implements FilterParserVisitor{ 5 | public Object defaultVisit(SimpleNode node, VisitorData data){ 6 | node.childrenAccept(this, data); 7 | return data; 8 | } 9 | public Object visit(SimpleNode node, VisitorData data){ 10 | return defaultVisit(node, data); 11 | } 12 | public Object visit(ASTExpression node, VisitorData data){ 13 | return defaultVisit(node, data); 14 | } 15 | public Object visit(ASTComparison node, VisitorData data){ 16 | return defaultVisit(node, data); 17 | } 18 | public Object visit(ASTAlias node, VisitorData data){ 19 | return defaultVisit(node, data); 20 | } 21 | } 22 | /* JavaCC - OriginalChecksum=06ac44fa53bd2978a8b7d3aa6de4fced (do not edit this line) */ 23 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/FilterParserTreeConstants.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. FilterParserTreeConstants.java Version 5.0 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | public interface FilterParserTreeConstants 5 | { 6 | public int JJTVOID = 0; 7 | public int JJTEXPRESSION = 1; 8 | public int JJTCOMPARISON = 2; 9 | public int JJTALIAS = 3; 10 | 11 | 12 | public String[] jjtNodeName = { 13 | "void", 14 | "Expression", 15 | "Comparison", 16 | "Alias", 17 | }; 18 | } 19 | /* JavaCC - OriginalChecksum=a8f71270ce0cb112d3284149dfad33e2 (do not edit this line) */ 20 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/FilterParserVisitor.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. FilterParserVisitor.java Version 5.0 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | public interface FilterParserVisitor 5 | { 6 | public Object visit(SimpleNode node, VisitorData data); 7 | public Object visit(ASTExpression node, VisitorData data); 8 | public Object visit(ASTComparison node, VisitorData data); 9 | public Object visit(ASTAlias node, VisitorData data); 10 | } 11 | /* JavaCC - OriginalChecksum=1d4b7dd7f55608a77745498f1be093dd (do not edit this line) */ 12 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/JJTFilterParserState.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. JJTFilterParserState.java Version 5.0 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | public class JJTFilterParserState { 5 | private java.util.List nodes; 6 | private java.util.List marks; 7 | 8 | private int sp; // number of nodes on stack 9 | private int mk; // current mark 10 | private boolean node_created; 11 | 12 | public JJTFilterParserState() { 13 | nodes = new java.util.ArrayList(); 14 | marks = new java.util.ArrayList(); 15 | sp = 0; 16 | mk = 0; 17 | } 18 | 19 | /* Determines whether the current node was actually closed and 20 | pushed. This should only be called in the final user action of a 21 | node scope. */ 22 | public boolean nodeCreated() { 23 | return node_created; 24 | } 25 | 26 | /* Call this to reinitialize the node stack. It is called 27 | automatically by the parser's ReInit() method. */ 28 | public void reset() { 29 | nodes.clear(); 30 | marks.clear(); 31 | sp = 0; 32 | mk = 0; 33 | } 34 | 35 | /* Returns the root node of the AST. It only makes sense to call 36 | this after a successful parse. */ 37 | public Node rootNode() { 38 | return nodes.get(0); 39 | } 40 | 41 | /* Pushes a node on to the stack. */ 42 | public void pushNode(Node n) { 43 | nodes.add(n); 44 | ++sp; 45 | } 46 | 47 | /* Returns the node on the top of the stack, and remove it from the 48 | stack. */ 49 | public Node popNode() { 50 | if (--sp < mk) { 51 | mk = marks.remove(marks.size()-1); 52 | } 53 | return nodes.remove(nodes.size()-1); 54 | } 55 | 56 | /* Returns the node currently on the top of the stack. */ 57 | public Node peekNode() { 58 | return nodes.get(nodes.size()-1); 59 | } 60 | 61 | /* Returns the number of children on the stack in the current node 62 | scope. */ 63 | public int nodeArity() { 64 | return sp - mk; 65 | } 66 | 67 | 68 | public void clearNodeScope(Node n) { 69 | while (sp > mk) { 70 | popNode(); 71 | } 72 | mk = marks.remove(marks.size()-1); 73 | } 74 | 75 | 76 | public void openNodeScope(Node n) { 77 | marks.add(mk); 78 | mk = sp; 79 | n.jjtOpen(); 80 | } 81 | 82 | 83 | /* A definite node is constructed from a specified number of 84 | children. That number of nodes are popped from the stack and 85 | made the children of the definite node. Then the definite node 86 | is pushed on to the stack. */ 87 | public void closeNodeScope(Node n, int num) { 88 | mk = marks.remove(marks.size()-1); 89 | while (num-- > 0) { 90 | Node c = popNode(); 91 | c.jjtSetParent(n); 92 | n.jjtAddChild(c, num); 93 | } 94 | n.jjtClose(); 95 | pushNode(n); 96 | node_created = true; 97 | } 98 | 99 | 100 | /* A conditional node is constructed if its condition is true. All 101 | the nodes that have been pushed since the node was opened are 102 | made children of the conditional node, which is then pushed 103 | on to the stack. If the condition is false the node is not 104 | constructed and they are left on the stack. */ 105 | public void closeNodeScope(Node n, boolean condition) { 106 | if (condition) { 107 | int a = nodeArity(); 108 | mk = marks.remove(marks.size()-1); 109 | while (a-- > 0) { 110 | Node c = popNode(); 111 | c.jjtSetParent(n); 112 | n.jjtAddChild(c, a); 113 | } 114 | n.jjtClose(); 115 | pushNode(n); 116 | node_created = true; 117 | } else { 118 | mk = marks.remove(marks.size()-1); 119 | node_created = false; 120 | } 121 | } 122 | } 123 | /* JavaCC - OriginalChecksum=4bd4e2bc0a8feefbb219e6f891cd825b (do not edit this line) */ 124 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/Node.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree: Do not edit this line. Node.java Version 7.0 */ 2 | /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | /* All AST nodes must implement this interface. It provides basic 6 | machinery for constructing the parent and child relationships 7 | between nodes. */ 8 | 9 | public 10 | interface Node { 11 | 12 | /** This method is called after the node has been made the current 13 | node. It indicates that child nodes can now be added to it. */ 14 | public void jjtOpen(); 15 | 16 | /** This method is called after all the child nodes have been 17 | added. */ 18 | public void jjtClose(); 19 | 20 | /** This pair of methods are used to inform the node of its 21 | parent. */ 22 | public void jjtSetParent(Node n); 23 | public Node jjtGetParent(); 24 | 25 | /** This method tells the node to add its argument to the node's 26 | list of children. */ 27 | public void jjtAddChild(Node n, int i); 28 | 29 | /** This method returns a child node. The children are numbered 30 | from zero, left to right. */ 31 | public Node jjtGetChild(int i); 32 | 33 | /** Return the number of children the node has. */ 34 | public int jjtGetNumChildren(); 35 | 36 | public int getId(); 37 | 38 | public String getFilterString(); 39 | 40 | /** Accept the visitor. **/ 41 | public Object jjtAccept(FilterParserVisitor visitor, VisitorData data); 42 | } 43 | /* JavaCC - OriginalChecksum=14b049954a3cc3bef39f1a1a74457d0b (do not edit this line) */ 44 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/SanityCheckVisitor.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. FilterParserDefaultVisitor.java Version 7.0.2 */ 2 | package com.nccgroup.loggerplusplus.filter.parser; 3 | 4 | import com.nccgroup.loggerplusplus.filter.ComparisonOperator; 5 | import com.nccgroup.loggerplusplus.logentry.LogEntryField; 6 | 7 | import java.util.Collection; 8 | import java.util.Date; 9 | import java.util.regex.Pattern; 10 | 11 | public class SanityCheckVisitor implements FilterParserVisitor{ 12 | 13 | public VisitorData defaultVisit(SimpleNode node, VisitorData data){ 14 | node.childrenAccept(this, data); 15 | return data; 16 | } 17 | public VisitorData visit(SimpleNode node, VisitorData data){ 18 | return defaultVisit(node, data); 19 | } 20 | 21 | public VisitorData visit(SimpleNode node){ 22 | return visit(node, new VisitorData()); 23 | } 24 | 25 | public VisitorData visit(ASTExpression node, VisitorData data){ 26 | return defaultVisit(node, data); 27 | } 28 | public VisitorData visit(ASTComparison node, VisitorData visitorData) { 29 | defaultVisit(node, visitorData); 30 | 31 | Class leftType, rightType; 32 | leftType = (node.left instanceof LogEntryField) ? ((LogEntryField) node.left).getType() : node.left.getClass(); 33 | rightType = (node.right instanceof LogEntryField) ? ((LogEntryField) node.right).getType() : node.right.getClass(); 34 | if (leftType == null || rightType == null) return visitorData; 35 | 36 | if (node.comparisonOperator == ComparisonOperator.LESS_THAN || node.comparisonOperator == ComparisonOperator.LESS_THAN_EQUAL 37 | || node.comparisonOperator == ComparisonOperator.GREATER_THAN || node.comparisonOperator == ComparisonOperator.GREATER_THAN_EQUAL) { 38 | boolean valid = (Date.class.isAssignableFrom(leftType) || Date.class.isAssignableFrom(rightType)) 39 | || (Number.class.isAssignableFrom(leftType) && Number.class.isAssignableFrom(rightType)); 40 | if (!valid) { 41 | visitorData.addError(String.format("Operator %s cannot be used to compare groups of type %s and %s.", 42 | node.comparisonOperator, leftType.getTypeName(), rightType.getTypeName())); 43 | } 44 | } else if (node.left instanceof Pattern) { 45 | visitorData.addError("The left operand of a comparison cannot be a pattern."); 46 | } else if (node.right instanceof Pattern && !String.class.isAssignableFrom(leftType)) { 47 | visitorData.addError("Regular expressions can only be used on string elements."); 48 | } else if (node.comparisonOperator == ComparisonOperator.CONTAINS && !(String.class.isAssignableFrom(leftType) && String.class.isAssignableFrom(leftType))) { 49 | visitorData.addError("The CONTAINS operator can only be used on string elements."); 50 | } else if (node.comparisonOperator == ComparisonOperator.IN && Collection.class.isAssignableFrom(rightType)) { 51 | visitorData.addError("The IN operator requires the right-hand object of the comparison to be a collection (e.g. list, set)!"); 52 | } 53 | 54 | return visitorData; 55 | } 56 | 57 | @Override 58 | public VisitorData visit(ASTAlias node, VisitorData data) { 59 | return data; 60 | } 61 | } 62 | /* JavaCC - OriginalChecksum=b30458d637879c9662107beea18204f0 (do not edit this line) */ 63 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/SimpleNode.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JJTree: Do not edit this line. SimpleNode.java Version 7.0 */ 2 | /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | public abstract class SimpleNode implements Node { 6 | 7 | protected Node parent; 8 | protected Node[] children; 9 | protected int id; 10 | protected Object value; 11 | protected FilterParser parser; 12 | 13 | public SimpleNode(int i) { 14 | id = i; 15 | } 16 | 17 | public SimpleNode(FilterParser p, int i) { 18 | this(i); 19 | parser = p; 20 | } 21 | 22 | public void jjtOpen() { 23 | } 24 | 25 | public void jjtClose() { 26 | } 27 | 28 | public void jjtSetParent(Node n) { parent = n; } 29 | public Node jjtGetParent() { return parent; } 30 | 31 | public void jjtAddChild(Node n, int i) { 32 | if (children == null) { 33 | children = new Node[i + 1]; 34 | } else if (i >= children.length) { 35 | Node c[] = new Node[i + 1]; 36 | System.arraycopy(children, 0, c, 0, children.length); 37 | children = c; 38 | } 39 | children[i] = n; 40 | } 41 | 42 | public Node jjtGetChild(int i) { 43 | return children[i]; 44 | } 45 | 46 | public int jjtGetNumChildren() { 47 | return (children == null) ? 0 : children.length; 48 | } 49 | 50 | public void jjtSetValue(Object value) { this.value = value; } 51 | public Object jjtGetValue() { return value; } 52 | 53 | /** Accept the visitor. **/ 54 | public Object jjtAccept(FilterParserVisitor visitor, VisitorData data) 55 | { 56 | return visitor.visit(this, data); 57 | } 58 | 59 | /** Accept the visitor. **/ 60 | public Object childrenAccept(FilterParserVisitor visitor, VisitorData data) 61 | { 62 | if (children != null) { 63 | for (int i = 0; i < children.length; ++i) { 64 | children[i].jjtAccept(visitor, data); 65 | } 66 | } 67 | return data; 68 | } 69 | 70 | /* You can override these two methods in subclasses of SimpleNode to 71 | customize the way the node appears when the tree is dumped. If 72 | your output uses more than one line you should override 73 | toString(String), otherwise overriding toString() is probably all 74 | you need to do. */ 75 | 76 | public String toString() { 77 | return FilterParserTreeConstants.jjtNodeName[id]; 78 | } 79 | public String toString(String prefix) { return prefix + toString(); } 80 | 81 | /* Override this method if you want to customize how the node dumps 82 | out its children. */ 83 | 84 | public void dump(String prefix) { 85 | System.out.println(toString(prefix)); 86 | if (children != null) { 87 | for (int i = 0; i < children.length; ++i) { 88 | SimpleNode n = (SimpleNode)children[i]; 89 | if (n != null) { 90 | n.dump(prefix + " "); 91 | } 92 | } 93 | } 94 | } 95 | 96 | public int getId() { 97 | return id; 98 | } 99 | } 100 | 101 | /* JavaCC - OriginalChecksum=dd4a72dc91922f5bdce98b5fa723a79c (do not edit this line) */ 102 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/Token.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. Token.java Version 7.0 */ 2 | /* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | /** 6 | * Describes the input token stream. 7 | */ 8 | 9 | public class Token implements java.io.Serializable { 10 | 11 | /** 12 | * The version identifier for this Serializable class. 13 | * Increment only if the serialized form of the 14 | * class changes. 15 | */ 16 | private static final long serialVersionUID = 1L; 17 | 18 | /** 19 | * An integer that describes the kind of this token. This numbering 20 | * system is determined by JavaCCParser, and a table of these numbers is 21 | * stored in the file ...Constants.java. 22 | */ 23 | public int kind; 24 | 25 | /** The line number of the first character of this Token. */ 26 | public int beginLine; 27 | /** The column number of the first character of this Token. */ 28 | public int beginColumn; 29 | /** The line number of the last character of this Token. */ 30 | public int endLine; 31 | /** The column number of the last character of this Token. */ 32 | public int endColumn; 33 | 34 | /** 35 | * The string image of the token. 36 | */ 37 | public String image; 38 | 39 | /** 40 | * A reference to the next regular (non-special) token from the input 41 | * stream. If this is the last token from the input stream, or if the 42 | * token manager has not read tokens beyond this one, this field is 43 | * set to null. This is true only if this token is also a regular 44 | * token. Otherwise, see below for a description of the contents of 45 | * this field. 46 | */ 47 | public Token next; 48 | 49 | /** 50 | * This field is used to access special tokens that occur prior to this 51 | * token, but after the immediately preceding regular (non-special) token. 52 | * If there are no such special tokens, this field is set to null. 53 | * When there are more than one such special token, this field refers 54 | * to the last of these special tokens, which in turn refers to the next 55 | * previous special token through its specialToken field, and so on 56 | * until the first special token (whose specialToken field is null). 57 | * The next fields of special tokens refer to other special tokens that 58 | * immediately follow it (without an intervening regular token). If there 59 | * is no such token, this field is null. 60 | */ 61 | public Token specialToken; 62 | 63 | /** 64 | * An optional attribute value of the Token. 65 | * Tokens which are not used as syntactic sugar will often contain 66 | * meaningful groups that will be used later on by the compiler or 67 | * interpreter. This attribute value is often different from the image. 68 | * Any subclass of Token that actually wants to return a non-null value can 69 | * override this method as appropriate. 70 | */ 71 | public Object getValue() { 72 | return null; 73 | } 74 | 75 | /** 76 | * No-argument constructor 77 | */ 78 | public Token() {} 79 | 80 | /** 81 | * Constructs a new token for the specified Image. 82 | */ 83 | public Token(int kind) 84 | { 85 | this(kind, null); 86 | } 87 | 88 | /** 89 | * Constructs a new token for the specified Image and Kind. 90 | */ 91 | public Token(int kind, String image) 92 | { 93 | this.kind = kind; 94 | this.image = image; 95 | } 96 | 97 | /** 98 | * Returns the image. 99 | */ 100 | public String toString() 101 | { 102 | return image; 103 | } 104 | 105 | /** 106 | * Returns a new Token object, by default. However, if you want, you 107 | * can create and return subclass objects based on the value of ofKind. 108 | * Simply add the cases to the switch for all those special cases. 109 | * For example, if you have a subclass of Token called IDToken that 110 | * you want to create if ofKind is ID, simply add something like : 111 | * 112 | * case MyParserConstants.ID : return new IDToken(ofKind, image); 113 | * 114 | * to the following switch statement. Then you can cast matchedToken 115 | * variable to the appropriate type and use sit in your lexical actions. 116 | */ 117 | public static Token newToken(int ofKind, String image) 118 | { 119 | switch(ofKind) 120 | { 121 | default : return new Token(ofKind, image); 122 | } 123 | } 124 | 125 | public static Token newToken(int ofKind) 126 | { 127 | return newToken(ofKind, null); 128 | } 129 | 130 | } 131 | /* JavaCC - OriginalChecksum=ad0fa790578b537791f3359d11e09484 (do not edit this line) */ 132 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/TokenMgrError.java: -------------------------------------------------------------------------------- 1 | /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 7.0 */ 2 | /* JavaCCOptions: */ 3 | package com.nccgroup.loggerplusplus.filter.parser; 4 | 5 | /** Token Manager Error. */ 6 | public class TokenMgrError extends Error 7 | { 8 | 9 | /** 10 | * The version identifier for this Serializable class. 11 | * Increment only if the serialized form of the 12 | * class changes. 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | /* 17 | * Ordinals for various reasons why an Error of this type can be thrown. 18 | */ 19 | 20 | /** 21 | * Lexical error occurred. 22 | */ 23 | public static final int LEXICAL_ERROR = 0; 24 | 25 | /** 26 | * An attempt was made to create a second instance of a static token manager. 27 | */ 28 | public static final int STATIC_LEXER_ERROR = 1; 29 | 30 | /** 31 | * Tried to change to an invalid lexical state. 32 | */ 33 | public static final int INVALID_LEXICAL_STATE = 2; 34 | 35 | /** 36 | * Detected (and bailed out of) an infinite loop in the token manager. 37 | */ 38 | public static final int LOOP_DETECTED = 3; 39 | 40 | /** 41 | * Indicates the reason why the exception is thrown. It will have 42 | * one of the above 4 groups. 43 | */ 44 | int errorCode; 45 | 46 | /** 47 | * Replaces unprintable characters by their escaped (or unicode escaped) 48 | * equivalents in the given string 49 | */ 50 | protected static final String addEscapes(String str) { 51 | StringBuffer retval = new StringBuffer(); 52 | char ch; 53 | for (int i = 0; i < str.length(); i++) { 54 | switch (str.charAt(i)) 55 | { 56 | case '\b': 57 | retval.append("\\b"); 58 | continue; 59 | case '\t': 60 | retval.append("\\t"); 61 | continue; 62 | case '\n': 63 | retval.append("\\n"); 64 | continue; 65 | case '\f': 66 | retval.append("\\f"); 67 | continue; 68 | case '\r': 69 | retval.append("\\r"); 70 | continue; 71 | case '\"': 72 | retval.append("\\\""); 73 | continue; 74 | case '\'': 75 | retval.append("\\\'"); 76 | continue; 77 | case '\\': 78 | retval.append("\\\\"); 79 | continue; 80 | default: 81 | if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { 82 | String s = "0000" + Integer.toString(ch, 16); 83 | retval.append("\\u" + s.substring(s.length() - 4, s.length())); 84 | } else { 85 | retval.append(ch); 86 | } 87 | continue; 88 | } 89 | } 90 | return retval.toString(); 91 | } 92 | 93 | /** 94 | * Returns a detailed message for the Error when it is thrown by the 95 | * token manager to indicate a lexical error. 96 | * Parameters : 97 | * EOFSeen : indicates if EOF caused the lexical error 98 | * curLexState : lexical state in which this error occurred 99 | * errorLine : line number when the error occurred 100 | * errorColumn : column number when the error occurred 101 | * errorAfter : prefix that was seen before this error occurred 102 | * curchar : the offending character 103 | * Note: You can customize the lexical error message by modifying this method. 104 | */ 105 | protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar) { 106 | char curChar1 = (char)curChar; 107 | return("Lexical error at line " + 108 | errorLine + ", column " + 109 | errorColumn + ". Encountered: " + 110 | (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar1)) + "\"") + " (" + (int)curChar + "), ") + 111 | "after : \"" + addEscapes(errorAfter) + "\""); 112 | } 113 | 114 | /** 115 | * You can also modify the body of this method to customize your error messages. 116 | * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not 117 | * of end-users concern, so you can return something like : 118 | * 119 | * "Internal Error : Please file a bug report .... " 120 | * 121 | * from this method for such cases in the release version of your parser. 122 | */ 123 | public String getMessage() { 124 | return super.getMessage(); 125 | } 126 | 127 | /* 128 | * Constructors of various flavors follow. 129 | */ 130 | 131 | /** No arg constructor. */ 132 | public TokenMgrError() { 133 | } 134 | 135 | /** Constructor with message and reason. */ 136 | public TokenMgrError(String message, int reason) { 137 | super(message); 138 | errorCode = reason; 139 | } 140 | 141 | /** Full Constructor. */ 142 | public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) { 143 | this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); 144 | } 145 | } 146 | /* JavaCC - OriginalChecksum=953dd5619161d3bf95d01cc7354e7d76 (do not edit this line) */ 147 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/parser/VisitorData.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.parser; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | 6 | public class VisitorData { 7 | 8 | private ArrayList errors = new ArrayList<>(); 9 | private boolean success = true; 10 | private HashMap data = new HashMap<>(); 11 | 12 | VisitorData(){ 13 | 14 | } 15 | 16 | public void addError(String error){ 17 | this.errors.add(error); 18 | this.success = false; 19 | } 20 | 21 | public void setSuccess(boolean success){ 22 | this.success = success; 23 | } 24 | 25 | public boolean isSuccess() { 26 | return success; 27 | } 28 | 29 | public ArrayList getErrors() { 30 | return errors; 31 | } 32 | 33 | public String getErrorString(){ 34 | StringBuilder sb = new StringBuilder(); 35 | for (int i = 0; i < errors.size(); i++) { 36 | sb.append(errors.get(i)); 37 | if(i != errors.size()-1) sb.append("\n"); 38 | } 39 | return sb.toString(); 40 | } 41 | 42 | public HashMap getData() { 43 | return data; 44 | } 45 | 46 | public void setData(String key, Object value){ 47 | this.data.put(key, value); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/savedfilter/SavedFilter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.savedfilter; 2 | 3 | import com.nccgroup.loggerplusplus.filter.FilterExpression; 4 | import com.nccgroup.loggerplusplus.filter.FilterRule; 5 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 6 | 7 | /** 8 | * Created by corey on 19/07/17. 9 | */ 10 | public class SavedFilter extends FilterRule { 11 | 12 | public SavedFilter(String name, String filterString) { 13 | super(name.replaceAll("[^a-zA-Z0-9_.]", "_")); 14 | this.trySetFilter(filterString); 15 | } 16 | 17 | public SavedFilter(String name, FilterExpression filterExpression){ 18 | super(name, filterExpression); 19 | } 20 | 21 | @Override 22 | public void setName(String name) { 23 | name = name.replaceAll("[^a-zA-Z0-9_.]", "_"); 24 | super.setName(name); 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if(o instanceof SavedFilter){ 30 | SavedFilter other = (SavedFilter) o; 31 | return other.getName().equals(getName()) && other.getFilterString().equals(getFilterString()); 32 | }else{ 33 | return super.equals(o); 34 | } 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "SavedFilter[" + this.getName() + ", " + (this.getFilterExpression() == null ? this.getFilterString() : this.getFilterExpression()) + "]"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/tag/Tag.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.tag; 2 | 3 | import com.nccgroup.loggerplusplus.filter.ColorizingFilterRule; 4 | import com.nccgroup.loggerplusplus.filter.FilterExpression; 5 | import com.nccgroup.loggerplusplus.filter.logfilter.LogTableFilter; 6 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 7 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 8 | import lombok.Getter; 9 | 10 | import java.awt.*; 11 | import java.util.UUID; 12 | 13 | /** 14 | * Created by corey on 19/07/17. 15 | */ 16 | public class Tag extends ColorizingFilterRule implements Comparable { 17 | 18 | @Getter 19 | private boolean shouldRetest = true; 20 | 21 | public Tag(){ 22 | super("", ""); 23 | } 24 | 25 | public Tag(String title, String filterString) { 26 | super(title, filterString); 27 | } 28 | 29 | public Tag(String title, FilterExpression filterExpression) { 30 | super(title, filterExpression); 31 | } 32 | 33 | @Override 34 | public boolean trySetFilter(String filterString){ 35 | boolean success = super.trySetFilter(filterString); 36 | if(success) shouldRetest = true; 37 | return success; 38 | } 39 | 40 | @Override 41 | public void setFilter(FilterExpression filter) { 42 | super.setFilter(filter); 43 | shouldRetest = true; 44 | } 45 | 46 | @Override 47 | public void setEnabled(boolean enabled) { 48 | super.setEnabled(enabled); 49 | shouldRetest = true; 50 | } 51 | 52 | @Override 53 | public void setPriority(short priority) { 54 | super.setPriority(priority); 55 | shouldRetest = true; 56 | } 57 | 58 | @Override 59 | public int compareTo(Tag tag) { 60 | return ((Comparable) this.getPriority()).compareTo(tag.getPriority()); 61 | } 62 | 63 | public boolean shouldRetest() { 64 | return shouldRetest; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return this.getName(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filter/tag/TagListener.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filter.tag; 2 | 3 | /** 4 | * Created by corey on 20/07/17. 5 | */ 6 | public interface TagListener { 7 | 8 | void onTagChange(Tag filter); 9 | 10 | void onTagAdd(Tag filter); 11 | 12 | void onTagRemove(Tag filter); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filterlibrary/FilterLibraryListener.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filterlibrary; 2 | 3 | import com.nccgroup.loggerplusplus.filter.savedfilter.SavedFilter; 4 | 5 | public interface FilterLibraryListener { 6 | void onFilterAdded(SavedFilter savedFilter, int index); 7 | void onFilterRemoved(SavedFilter savedFilter, int index); 8 | void onFilterModified(SavedFilter savedFilter, int index); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filterlibrary/FilterLibraryPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filterlibrary; 2 | 3 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 4 | import com.nccgroup.loggerplusplus.filter.savedfilter.SavedFilter; 5 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.ButtonRenderer; 6 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.FilterRenderer; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.awt.event.MouseAdapter; 11 | import java.awt.event.MouseEvent; 12 | 13 | /** 14 | * Created by corey on 27/08/17. 15 | */ 16 | public class FilterLibraryPanel extends JPanel { 17 | 18 | private final FilterLibraryController libraryController; 19 | 20 | public FilterLibraryPanel(FilterLibraryController libraryController){ 21 | super(new BorderLayout()); 22 | 23 | this.libraryController = libraryController; 24 | 25 | JTable libraryTable = new JTable(new FilterLibraryTableModel(this.libraryController)); 26 | libraryTable.setRowHeight(25); 27 | libraryTable.setFillsViewportHeight(true); 28 | libraryTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 29 | libraryTable.setAutoCreateRowSorter(false); 30 | libraryTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 31 | ((JComponent) libraryTable.getDefaultRenderer(JButton.class)).setOpaque(true); 32 | libraryTable.getColumnModel().getColumn(1).setCellRenderer(new FilterRenderer()); 33 | libraryTable.getColumnModel().getColumn(2).setCellRenderer(new ButtonRenderer()); 34 | libraryTable.getColumnModel().getColumn(3).setCellRenderer(new ButtonRenderer()); 35 | 36 | libraryTable.addMouseListener(new MouseAdapter() { 37 | @Override 38 | public void mouseReleased(MouseEvent mouseEvent) { 39 | if(SwingUtilities.isLeftMouseButton(mouseEvent)) { 40 | int col = libraryTable.columnAtPoint(mouseEvent.getPoint()); 41 | int row = libraryTable.rowAtPoint(mouseEvent.getPoint()); 42 | ((FilterLibraryTableModel) libraryTable.getModel()).onClick(row, col); 43 | } 44 | } 45 | }); 46 | 47 | JPanel controlPanel = new JPanel(new GridLayout(1,0)); 48 | JButton addFilterButton = new JButton("Add Snippet"); 49 | addFilterButton.setPreferredSize(new Dimension(0, 75)); 50 | addFilterButton.addActionListener(actionEvent -> { 51 | libraryController.addFilter(new SavedFilter("Unnamed", "Response.body CONTAINS \"Example\"")); 52 | }); 53 | JButton removeSelectedButton = new JButton("Remove Selected"); 54 | removeSelectedButton.setMinimumSize(new Dimension(0, 75)); 55 | removeSelectedButton.addActionListener(actionEvent -> { 56 | int selectedRow = libraryTable.getSelectedRow(); 57 | if(selectedRow == -1) return; 58 | SavedFilter filter = libraryController.getFilterSnippets().get(selectedRow); 59 | libraryController.removeFilter(filter); 60 | }); 61 | controlPanel.add(addFilterButton); 62 | controlPanel.add(removeSelectedButton); 63 | 64 | JScrollPane tableScrollPane = new JScrollPane(libraryTable); 65 | this.add(tableScrollPane, BorderLayout.CENTER); 66 | this.add(controlPanel, BorderLayout.SOUTH); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/filterlibrary/FilterLibraryTableModel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.filterlibrary; 2 | 3 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 4 | import com.nccgroup.loggerplusplus.filter.parser.ParseException; 5 | import com.nccgroup.loggerplusplus.filter.savedfilter.SavedFilter; 6 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.ColorFilterDialog; 7 | import com.nccgroup.loggerplusplus.util.MoreHelp; 8 | 9 | import javax.swing.*; 10 | import javax.swing.table.AbstractTableModel; 11 | 12 | public class FilterLibraryTableModel extends AbstractTableModel implements FilterLibraryListener { 13 | 14 | private final FilterLibraryController controller; 15 | JButton btnApplyFilter; 16 | JButton btnSetColorFilter; 17 | private final String[] columnNames = {"Alias", "Snippet", "", ""}; 18 | 19 | public FilterLibraryTableModel(FilterLibraryController controller){ 20 | this.controller = controller; 21 | this.controller.addFilterListener(this); 22 | btnApplyFilter = new JButton("Set as LogFilter"); 23 | btnSetColorFilter = new JButton("Use as Color LogFilter"); 24 | } 25 | 26 | @Override 27 | public int getRowCount() { 28 | return controller.getFilterSnippets().size(); 29 | } 30 | 31 | @Override 32 | public int getColumnCount() { 33 | return columnNames.length; 34 | } 35 | 36 | @Override 37 | public Object getValueAt(int row, int column) { 38 | if(row >= controller.getFilterSnippets().size()) return null; 39 | SavedFilter savedFilter = controller.getFilterSnippets().get(row); 40 | switch (column){ 41 | case 0: return savedFilter.getName(); 42 | case 1: { 43 | return savedFilter.getFilterExpression() == null ? savedFilter.getFilterString() : savedFilter.getFilterExpression(); 44 | } 45 | case 2: return btnApplyFilter; 46 | case 3: return btnSetColorFilter; 47 | } 48 | return null; 49 | } 50 | 51 | @Override 52 | public String getColumnName(int column) { 53 | return columnNames[column]; 54 | } 55 | 56 | @Override 57 | public boolean isCellEditable(int rowIndex, int columnIndex) { 58 | return columnIndex == 0 || columnIndex == 1; 59 | } 60 | 61 | @Override 62 | public void setValueAt(Object value, int row, int column) { 63 | SavedFilter savedFilter = controller.getFilterSnippets().get(row); 64 | if(savedFilter == null) return; 65 | if(column == 0) { 66 | savedFilter.setName((String) value); 67 | if(!((String) value).equalsIgnoreCase(savedFilter.getName())){ 68 | JOptionPane.showMessageDialog(LoggerPlusPlus.instance.getMainViewController().getUiComponent(), "Alias names may only contain alphanumeric characters and the symbols period (.) and underscore (_)\n" + 69 | "Invalid characters have been replaced with an underscore.", "Alias Error", JOptionPane.WARNING_MESSAGE); 70 | } 71 | } 72 | if(column == 1){ 73 | try{ 74 | savedFilter.parseAndSetFilter((String) value); 75 | controller.propagateChangesToSnippetUsers(savedFilter); 76 | }catch (ParseException e){ 77 | //Not a valid filter... 78 | MoreHelp.showLargeOutputDialog("Filter Exception", "" + e.getMessage().replaceAll("\n", "
") + ""); 79 | // JOptionPane.showMessageDialog(LoggerPlusPlus.instance.getMainViewController().getUiComponent(), "" + e.getMessage().replaceAll("\n", "
") + "", "Filter Exception", JOptionPane.ERROR_MESSAGE); 80 | } 81 | } 82 | controller.saveFilters(); 83 | } 84 | 85 | public void onClick(int row, int col) { 86 | if(row < 0 || row >= controller.getFilterSnippets().size()) return; 87 | SavedFilter savedFilter = controller.getFilterSnippets().get(row); 88 | if(col == 2){ 89 | LoggerPlusPlus.instance.getLogViewController().getLogFilterController().setFilter(savedFilter.getFilterString()); 90 | LoggerPlusPlus.instance.getMainViewController().getTabbedPanel().setSelectedIndex(0); 91 | return; 92 | } 93 | if(col == 3){ 94 | controller.addColorFilter(savedFilter.getName(), savedFilter.getFilterExpression()); 95 | ColorFilterDialog dialog = new ColorFilterDialog(LoggerPlusPlus.instance.getLibraryController()); 96 | dialog.setVisible(true); 97 | } 98 | } 99 | 100 | @Override 101 | public void onFilterAdded(SavedFilter savedFilter, int index) { 102 | int rows = getRowCount(); 103 | SwingUtilities.invokeLater(() -> { 104 | this.fireTableRowsInserted(index, index); 105 | }); 106 | } 107 | 108 | @Override 109 | public void onFilterModified(SavedFilter savedFilter, int index) { 110 | SwingUtilities.invokeLater(() -> { 111 | this.fireTableRowsUpdated(index, index); 112 | }); 113 | } 114 | 115 | @Override 116 | public void onFilterRemoved(SavedFilter savedFilter, int index) { 117 | SwingUtilities.invokeLater(() -> { 118 | this.fireTableRowsDeleted(index, index); 119 | }); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/grepper/GrepResults.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.grepper; 2 | 3 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class GrepResults { 8 | private final LogEntry entry; 9 | private final ArrayList results; 10 | private int requestMatches = 0; 11 | private int responseMatches = 0; 12 | 13 | 14 | public GrepResults(LogEntry entry) { 15 | this.entry = entry; 16 | this.results = new ArrayList<>(); 17 | } 18 | 19 | public ArrayList getMatches() { 20 | return results; 21 | } 22 | 23 | public LogEntry getLogEntry() { 24 | return this.entry; 25 | } 26 | 27 | public void addRequestMatch(Match match) { 28 | this.results.add(match); 29 | this.requestMatches++; 30 | } 31 | 32 | public void addResponseMatch(Match match) { 33 | this.results.add(match); 34 | this.responseMatches++; 35 | } 36 | 37 | public int getRequestMatches() { 38 | return requestMatches; 39 | } 40 | 41 | public int getResponseMatches() { 42 | return responseMatches; 43 | } 44 | 45 | public static class Match { 46 | public final String[] groups; 47 | public final int startIndex; 48 | public final int endIndex; 49 | public final boolean isRequest; 50 | 51 | Match(String[] groups, boolean isRequest, int startIndex, int endIndex) { 52 | this.groups = groups; 53 | this.isRequest = isRequest; 54 | this.startIndex = startIndex; 55 | this.endIndex = endIndex; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/grepper/GrepperListener.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.grepper; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | public interface GrepperListener { 6 | void onSearchStarted(Pattern pattern, int searchEntries); 7 | void onEntryProcessed(GrepResults entryResults); 8 | void onResetRequested(); 9 | void onSearchComplete(); 10 | void onShutdownInitiated(); 11 | void onShutdownComplete(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/grepper/UniquePatternMatchTable.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.grepper; 2 | 3 | 4 | import javax.swing.*; 5 | import javax.swing.table.AbstractTableModel; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.LinkedHashMap; 9 | import java.util.regex.Pattern; 10 | 11 | public class UniquePatternMatchTable extends JTable implements GrepperListener { 12 | private final GrepperController controller; 13 | private final ArrayList entryKeys; 14 | private final HashMap valueCountMap; 15 | 16 | public UniquePatternMatchTable(GrepperController controller){ 17 | super(); 18 | this.controller = controller; 19 | this.setModel(new UniqueValueTableModel()); 20 | this.setAutoCreateRowSorter(true); 21 | this.setColumnSelectionAllowed(true); 22 | this.entryKeys = new ArrayList<>(); 23 | this.valueCountMap = new LinkedHashMap<>(); 24 | 25 | this.controller.addListener(this); 26 | } 27 | 28 | public void reset(){ 29 | synchronized (this.valueCountMap) { 30 | this.valueCountMap.clear(); 31 | } 32 | synchronized (this.entryKeys){ 33 | this.entryKeys.clear(); 34 | } 35 | SwingUtilities.invokeLater(() -> ((UniqueValueTableModel) this.getModel()).fireTableDataChanged()); 36 | } 37 | 38 | public void addEntry(GrepResults entry) { 39 | synchronized (valueCountMap) { 40 | synchronized (entryKeys) { 41 | for (GrepResults.Match result : entry.getMatches()) { 42 | String key = result.groups[0]; 43 | int index = entryKeys.indexOf(key); 44 | if (index == -1) { 45 | entryKeys.add(key); 46 | valueCountMap.put(key, new UniqueMatch(result.groups)); 47 | } else { 48 | valueCountMap.get(key).increment(); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public void onSearchStarted(Pattern pattern, int searchEntries) { 57 | reset(); 58 | ((UniqueValueTableModel) getModel()).groups = pattern.matcher("").groupCount(); 59 | ((AbstractTableModel) this.getModel()).fireTableStructureChanged(); 60 | } 61 | 62 | @Override 63 | public void onEntryProcessed(GrepResults entryResults) { 64 | if(entryResults != null) addEntry(entryResults); 65 | } 66 | 67 | @Override 68 | public void onSearchComplete() { 69 | ((AbstractTableModel) this.getModel()).fireTableDataChanged(); 70 | } 71 | 72 | @Override 73 | public void onResetRequested() { 74 | reset(); 75 | } 76 | 77 | @Override 78 | public void onShutdownInitiated() { 79 | 80 | } 81 | 82 | @Override 83 | public void onShutdownComplete() { 84 | ((AbstractTableModel) this.getModel()).fireTableDataChanged(); 85 | } 86 | 87 | 88 | public class UniqueValueTableModel extends AbstractTableModel { 89 | 90 | int groups = 0; 91 | 92 | @Override 93 | public String getColumnName(int column) { 94 | if(column == 0) return "Value"; 95 | if(column == groups+1) return "Count"; 96 | return "Group " + column; 97 | } 98 | 99 | @Override 100 | public int getRowCount() { 101 | if(valueCountMap == null) return 0; 102 | else return valueCountMap.size(); 103 | } 104 | 105 | @Override 106 | public int getColumnCount() { 107 | return groups+2; 108 | } 109 | 110 | @Override 111 | public Class getColumnClass(int columnIndex) { 112 | if(columnIndex == groups+1) return Integer.class; 113 | return String.class; 114 | } 115 | 116 | @Override 117 | public Object getValueAt(int row, int col) { 118 | String key = entryKeys.get(row); 119 | if(col == 0) return key; 120 | UniqueMatch match = valueCountMap.get(key); 121 | if(col == groups+1) return match.count; 122 | return match.groups[col]; 123 | } 124 | } 125 | 126 | static class UniqueMatch { 127 | String key; 128 | String[] groups; 129 | int count; 130 | 131 | UniqueMatch(String[] match){ 132 | this.key = match[0]; 133 | this.groups = match; 134 | this.count = 1; 135 | } 136 | 137 | private void increment(){ 138 | this.count++; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logentry/FieldGroup.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logentry; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | 7 | public enum FieldGroup { 8 | ENTRY("Entry", "Log", "Proxy"), 9 | REQUEST("Request"), 10 | RESPONSE("Response"); 11 | private String label; 12 | private List additionalLabels; 13 | 14 | private static final HashMap groupLabelMap = new HashMap<>(); 15 | 16 | static { 17 | for (FieldGroup fieldGroup : FieldGroup.values()) { 18 | groupLabelMap.put(fieldGroup.label.toUpperCase(), fieldGroup); 19 | fieldGroup.additionalLabels.forEach(label -> groupLabelMap.put(label.toUpperCase(), fieldGroup)); 20 | } 21 | } 22 | 23 | FieldGroup(String primaryLabel, String... labels) { 24 | this.label = primaryLabel; 25 | additionalLabels = Arrays.asList(labels); 26 | } 27 | 28 | public String getLabel() { 29 | return label; 30 | } 31 | 32 | public static FieldGroup findByLabel(String label) { 33 | return groupLabelMap.get(label.toUpperCase()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntryFieldSerializer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logentry; 2 | 3 | import com.google.gson.*; 4 | 5 | import java.lang.reflect.Type; 6 | 7 | public class LogEntryFieldSerializer implements JsonSerializer, JsonDeserializer { 8 | 9 | @Override 10 | public LogEntryField deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 11 | return LogEntryField.getByFullyQualifiedName(json.getAsString()); 12 | } 13 | 14 | @Override 15 | public JsonElement serialize(LogEntryField src, Type typeOfSrc, JsonSerializationContext context) { 16 | return context.serialize(src.getFullLabel()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logentry/LogEntrySerializer.java: -------------------------------------------------------------------------------- 1 | // 2 | // Burp Suite Logger++ 3 | // 4 | // Released as open source by NCC Group Plc - https://www.nccgroup.trust/ 5 | // 6 | // Originally Developed by Soroush Dalili (@irsdl) 7 | // Maintained by Corey Arthur (@CoreyD97) 8 | // 9 | // Project link: http://www.github.com/nccgroup/BurpSuiteLoggerPlusPlus 10 | // 11 | // Released under AGPL see LICENSE for more information 12 | // 13 | 14 | package com.nccgroup.loggerplusplus.logentry; 15 | 16 | import com.google.gson.JsonElement; 17 | import com.google.gson.JsonObject; 18 | import com.google.gson.JsonSerializationContext; 19 | import com.google.gson.JsonSerializer; 20 | 21 | import java.lang.reflect.Type; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | public class LogEntrySerializer implements JsonSerializer { 26 | 27 | private final List excludedFields = Arrays.asList(LogEntryField.NUMBER); 28 | 29 | @Override 30 | public JsonElement serialize(LogEntry src, Type typeOfSrc, JsonSerializationContext context) { 31 | JsonObject entry = new JsonObject(); 32 | for (FieldGroup group : FieldGroup.values()) { 33 | JsonObject groupEntries = new JsonObject(); 34 | for (LogEntryField fieldInGroup : LogEntryField.getFieldsInGroup(group)) { 35 | if(excludedFields.contains(fieldInGroup)) continue; 36 | groupEntries.add(fieldInGroup.getLabels()[0], context.serialize(src.getValueByKey(fieldInGroup))); 37 | } 38 | entry.add(group.getLabel(), groupEntries); 39 | } 40 | return entry; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logentry/Status.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logentry; 2 | 3 | public enum Status { 4 | UNPROCESSED, AWAITING_RESPONSE, PROCESSED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logging/BurpAppender.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logging; 2 | 3 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 4 | import org.apache.logging.log4j.Level; 5 | import org.apache.logging.log4j.core.Appender; 6 | import org.apache.logging.log4j.core.Core; 7 | import org.apache.logging.log4j.core.Filter; 8 | import org.apache.logging.log4j.core.LogEvent; 9 | import org.apache.logging.log4j.core.appender.AbstractAppender; 10 | import org.apache.logging.log4j.core.config.plugins.Plugin; 11 | import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 12 | import org.apache.logging.log4j.core.config.plugins.PluginElement; 13 | import org.apache.logging.log4j.core.config.plugins.PluginFactory; 14 | import org.apache.logging.log4j.core.layout.PatternLayout; 15 | 16 | @Plugin(name="BurpAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) 17 | public class BurpAppender extends AbstractAppender { 18 | 19 | public BurpAppender(String name, Filter filter){ 20 | super(name, filter, PatternLayout.createDefaultLayout(), false, null); 21 | } 22 | 23 | @PluginFactory 24 | public static BurpAppender createAppender(@PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) { 25 | return new BurpAppender(name, filter); 26 | } 27 | 28 | @Override 29 | public void append(LogEvent event) { 30 | String message = new String(this.getLayout().toByteArray(event)); 31 | if(LoggerPlusPlus.montoya == null) return; 32 | 33 | if (event.getLevel().isInRange(Level.WARN, Level.FATAL)) { 34 | LoggerPlusPlus.montoya.logging().logToError(message); 35 | } else { 36 | LoggerPlusPlus.montoya.logging().logToOutput(message); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logging/LoggingController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logging; 2 | 3 | import burp.api.montoya.MontoyaApi; 4 | import com.coreyd97.BurpExtenderUtilities.IGsonProvider; 5 | import com.nccgroup.loggerplusplus.util.Globals; 6 | import lombok.extern.log4j.Log4j2; 7 | import org.apache.logging.log4j.Level; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.core.LoggerContext; 10 | import org.apache.logging.log4j.core.appender.ConsoleAppender; 11 | 12 | @Log4j2 13 | public class LoggingController { 14 | 15 | private final IGsonProvider gsonProvider; 16 | private Level logLevel; 17 | 18 | public LoggingController(IGsonProvider gsonProvider, MontoyaApi montoyaApi) { 19 | this.gsonProvider = gsonProvider; 20 | logLevel = gsonProvider.getGson().fromJson(montoyaApi.persistence().preferences().getString(Globals.PREF_LOG_LEVEL), Level.class); 21 | if(montoyaApi.extension().filename() == null){ //Loaded from classpath. Log to console! 22 | LoggerContext context = (LoggerContext) LogManager.getContext(false); 23 | ConsoleAppender.Builder appenderBuilder = new ConsoleAppender.Builder(); 24 | appenderBuilder.setName("ConsoleAppender"); 25 | ConsoleAppender consoleAppender = appenderBuilder.build(); 26 | consoleAppender.start(); 27 | context.getRootLogger().addAppender(consoleAppender); 28 | } 29 | setLogLevel(logLevel); 30 | } 31 | 32 | public void setLogLevel(Level logLevel) { 33 | this.logLevel = logLevel; 34 | LoggerContext context = (LoggerContext) LogManager.getContext(false); 35 | context.getConfiguration().getRootLogger().setLevel(logLevel); 36 | context.updateLoggers(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/LogTableFilterStatusListener.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview; 2 | 3 | public interface LogTableFilterStatusListener { 4 | void onFilteringStart(); 5 | void onFilteringFinish(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/LogViewController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.coreyd97.BurpExtenderUtilities.VariableViewPanel; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.filter.logfilter.LogFilterController; 7 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 8 | import com.nccgroup.loggerplusplus.logview.entryviewer.RequestViewerController; 9 | import com.nccgroup.loggerplusplus.logview.logtable.LogTableController; 10 | import lombok.Getter; 11 | 12 | public class LogViewController { 13 | 14 | @Getter 15 | private final FilterLibraryController filterLibraryController; 16 | 17 | @Getter 18 | private final Preferences preferences; 19 | 20 | @Getter 21 | private final LogFilterController logFilterController; 22 | 23 | @Getter 24 | private final LogTableController logTableController; 25 | 26 | @Getter 27 | private final RequestViewerController requestViewerController; 28 | 29 | @Getter 30 | private final LogViewPanel logViewPanel; 31 | 32 | public LogViewController(FilterLibraryController filterLibraryController){ 33 | this.filterLibraryController = filterLibraryController; 34 | this.preferences = LoggerPlusPlus.instance.getPreferencesController().getPreferences(); 35 | 36 | this.logTableController = new LogTableController(this, filterLibraryController); 37 | this.logFilterController = new LogFilterController(this); 38 | this.requestViewerController = new RequestViewerController(preferences); 39 | 40 | //Build UI 41 | this.logViewPanel = new LogViewPanel(this); 42 | } 43 | 44 | public void setPanelLayout(VariableViewPanel.View view){ 45 | this.logViewPanel.getTableViewerSplitPanel().setView(view); 46 | } 47 | 48 | public void setEntryViewerLayout(VariableViewPanel.View view){ 49 | this.logViewPanel.getRequestViewerPanel().getVariableViewPanel().setView(view); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/LogViewPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | import com.coreyd97.BurpExtenderUtilities.Preferences; 6 | import com.coreyd97.BurpExtenderUtilities.VariableViewPanel; 7 | import com.nccgroup.loggerplusplus.logview.entryviewer.RequestViewerPanel; 8 | import com.nccgroup.loggerplusplus.logview.logtable.LogTable; 9 | import com.nccgroup.loggerplusplus.util.Globals; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.awt.event.MouseAdapter; 14 | import java.awt.event.MouseEvent; 15 | 16 | /** 17 | * Created by corey on 24/08/17. 18 | */ 19 | public class LogViewPanel extends JPanel { 20 | 21 | private final LogViewController controller; 22 | private final Preferences preferences; 23 | private final MainControlsPanel mainControlsPanel; 24 | private final LogTable logTable; 25 | private final JScrollPane logTableScrollPane; 26 | private final RequestViewerPanel requestViewerPanel; 27 | private final VariableViewPanel tableViewerSplitPanel; 28 | 29 | public LogViewPanel(LogViewController controller){ 30 | this.controller = controller; 31 | this.preferences = controller.getPreferences(); 32 | 33 | mainControlsPanel = new MainControlsPanel(controller.getLogFilterController()); 34 | 35 | logTable = controller.getLogTableController().getLogTable(); 36 | 37 | logTableScrollPane = new JScrollPane(logTable,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 38 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);//View 39 | logTableScrollPane.addMouseWheelListener(mouseWheelEvent -> { 40 | JScrollBar scrollBar = logTableScrollPane.getVerticalScrollBar(); 41 | preferences.setSetting(Globals.PREF_AUTO_SCROLL, 42 | scrollBar.getValue() + scrollBar.getHeight() >= scrollBar.getMaximum()); 43 | }); 44 | logTableScrollPane.getVerticalScrollBar().addMouseListener(new MouseAdapter() { 45 | @Override 46 | public void mouseReleased(MouseEvent mouseEvent) { 47 | JScrollBar scrollBar = logTableScrollPane.getVerticalScrollBar(); 48 | preferences.setSetting(Globals.PREF_AUTO_SCROLL, 49 | scrollBar.getValue() + scrollBar.getHeight() >= scrollBar.getMaximum()); 50 | } 51 | }); 52 | 53 | requestViewerPanel = controller.getRequestViewerController().getRequestViewerPanel(); 54 | 55 | tableViewerSplitPanel = new VariableViewPanel(controller.getPreferences(), Globals.PREF_LAYOUT, 56 | logTableScrollPane, "Log Table", 57 | requestViewerPanel, "Request/Response", VariableViewPanel.View.VERTICAL); 58 | 59 | buildUI(); 60 | } 61 | 62 | private void buildUI(){ 63 | this.removeAll(); 64 | this.setLayout(new BorderLayout()); 65 | 66 | JPanel panel = PanelBuilder.build(new Component[][]{ 67 | new Component[]{mainControlsPanel}, 68 | new Component[]{tableViewerSplitPanel}, 69 | }, new int[][]{ 70 | new int[]{0}, 71 | new int[]{1} 72 | }, Alignment.FILL, 1.0, 1.0); 73 | 74 | this.add(panel, BorderLayout.CENTER); 75 | } 76 | 77 | public VariableViewPanel getTableViewerSplitPanel() { 78 | return tableViewerSplitPanel; 79 | } 80 | 81 | public RequestViewerPanel getRequestViewerPanel() { 82 | return requestViewerPanel; 83 | } 84 | 85 | public LogTable getLogTable() { 86 | return logTable; 87 | } 88 | 89 | public JScrollPane getScrollPane() { 90 | return logTableScrollPane; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/MainControlsPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview; 2 | 3 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 4 | import com.nccgroup.loggerplusplus.filter.logfilter.LogFilterController; 5 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.ColorFilterDialog; 6 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.TagDialog; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | public class MainControlsPanel extends JPanel { 12 | 13 | private final LogFilterController logFilterController; 14 | 15 | public MainControlsPanel(LogFilterController logFilterController){ 16 | super(new GridBagLayout()); 17 | 18 | this.logFilterController = logFilterController; 19 | GridBagConstraints gbc = new GridBagConstraints(); 20 | gbc.fill = GridBagConstraints.BOTH; 21 | gbc.gridx = 0; 22 | gbc.weightx = 0; 23 | gbc.weighty = 1; 24 | 25 | this.add(new JLabel(" Filter: "), gbc); 26 | 27 | gbc.gridx = 1; 28 | gbc.weightx = 99.0; 29 | this.add(logFilterController.getFilterField(), gbc); 30 | 31 | final JButton tagsButton = new JButton("Tags"); 32 | tagsButton.addActionListener(actionEvent -> new TagDialog(LoggerPlusPlus.instance.getLibraryController()).setVisible(true)); 33 | 34 | gbc.gridx = 2; 35 | gbc.weightx = 0; 36 | this.add(tagsButton, gbc); 37 | 38 | final JButton colorFilterButton = new JButton("Colorize"); 39 | colorFilterButton.addActionListener(actionEvent -> new ColorFilterDialog(LoggerPlusPlus.instance.getLibraryController()).setVisible(true)); 40 | 41 | gbc.gridx = 3; 42 | gbc.weightx = 0; 43 | this.add(colorFilterButton, gbc); 44 | 45 | final JButton clearLogsButton = new JButton("Clear Logs"); 46 | clearLogsButton.addActionListener(actionEvent -> 47 | logFilterController.getLogViewController().getLogTableController().reset()); 48 | 49 | gbc.gridx = 4; 50 | gbc.weightx = 0; 51 | this.add(clearLogsButton, gbc); 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.entryviewer; 2 | 3 | import burp.api.montoya.ui.editor.EditorOptions; 4 | import burp.api.montoya.ui.editor.HttpRequestEditor; 5 | import burp.api.montoya.ui.editor.HttpResponseEditor; 6 | import com.coreyd97.BurpExtenderUtilities.Preferences; 7 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 8 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 9 | import lombok.Getter; 10 | 11 | @Getter 12 | public class RequestViewerController { 13 | 14 | private final Preferences preferences; 15 | private final HttpRequestEditor requestEditor; 16 | private final HttpResponseEditor responseEditor; 17 | private final RequestViewerPanel requestViewerPanel; 18 | 19 | private LogEntry currentEntry; 20 | 21 | public RequestViewerController(Preferences preferences) { 22 | this.preferences = preferences; 23 | this.requestEditor = LoggerPlusPlus.montoya.userInterface().createHttpRequestEditor(EditorOptions.READ_ONLY); 24 | this.responseEditor = LoggerPlusPlus.montoya.userInterface().createHttpResponseEditor(EditorOptions.READ_ONLY); 25 | this.requestViewerPanel = new RequestViewerPanel(this); 26 | } 27 | 28 | public void setDisplayedEntity(LogEntry logEntry) { 29 | // Only update message if it's new. This fixes issue #164 and improves performance during heavy scanning. 30 | if (this.currentEntry == logEntry) { return; } 31 | 32 | this.currentEntry = logEntry; 33 | 34 | if (logEntry == null || logEntry.getRequest() == null) { 35 | requestEditor.setRequest(null); 36 | }else{ 37 | requestEditor.setRequest(logEntry.getRequest()); 38 | } 39 | 40 | if (logEntry == null || logEntry.getResponse() == null) { 41 | responseEditor.setResponse(null); 42 | }else{ 43 | responseEditor.setResponse(logEntry.getResponse()); 44 | } 45 | } 46 | 47 | public void setMarkers(){ 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/entryviewer/RequestViewerPanel.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.entryviewer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.PopOutPanel; 4 | import com.coreyd97.BurpExtenderUtilities.VariableViewPanel; 5 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 6 | import com.nccgroup.loggerplusplus.util.Globals; 7 | 8 | public class RequestViewerPanel extends PopOutPanel { 9 | 10 | private final RequestViewerController controller; 11 | private final VariableViewPanel variableViewPanel; 12 | 13 | public RequestViewerPanel(RequestViewerController controller){ 14 | super(LoggerPlusPlus.montoya); 15 | this.controller = controller; 16 | 17 | this.variableViewPanel = new VariableViewPanel(controller.getPreferences(), Globals.PREF_MESSAGE_VIEW_LAYOUT, 18 | controller.getRequestEditor().uiComponent(), "Request", 19 | controller.getResponseEditor().uiComponent(), "Response", 20 | VariableViewPanel.View.HORIZONTAL); 21 | 22 | this.setComponent(variableViewPanel); 23 | this.setTitle("Request/Response Viewer"); 24 | } 25 | 26 | public VariableViewPanel getVariableViewPanel() { 27 | return variableViewPanel; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableColumn.java: -------------------------------------------------------------------------------- 1 | // 2 | // Burp Suite Logger++ 3 | // 4 | // Released as open source by NCC Group Plc - https://www.nccgroup.trust/ 5 | // 6 | // Developed by Soroush Dalili (@irsdl) 7 | // 8 | // Project link: http://www.github.com/nccgroup/BurpSuiteLoggerPlusPlus 9 | // 10 | // Released under AGPL see LICENSE for more information 11 | // 12 | 13 | package com.nccgroup.loggerplusplus.logview.logtable; 14 | 15 | // To define a structure for table headers 16 | // This will provide a high degree of customisation 17 | // a sample JSON object which will be converted to this object is as follows: 18 | // "{'columnsDefinition':[{'id':'number','visibleName':'#','width':50,'type':'int','readonly':true,'order':1,'visible':true,'description':'Item index number','isRegEx':false,'regExData':{'regExString':'','regExCaseSensitive':false}}]}"; 19 | 20 | import com.google.gson.*; 21 | import com.nccgroup.loggerplusplus.logentry.LogEntryField; 22 | 23 | import javax.swing.table.TableColumn; 24 | import java.lang.reflect.Type; 25 | 26 | public class LogTableColumn extends TableColumn implements Comparable{ 27 | 28 | private String name; 29 | private int order; 30 | private String visibleName; 31 | private boolean visible; 32 | private boolean readOnly; 33 | private String description; 34 | private String defaultVisibleName; 35 | 36 | @Override 37 | public void setPreferredWidth(int width){ 38 | super.setPreferredWidth(width); 39 | } 40 | @Override 41 | public void setWidth(int width){ 42 | super.setWidth(width); 43 | this.setPreferredWidth(width); 44 | } 45 | 46 | @Override 47 | public LogEntryField getIdentifier() { return (LogEntryField) this.identifier; } 48 | public String getName() { 49 | return name; 50 | } 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | public String getVisibleName() { 55 | return visibleName; 56 | } 57 | public void setVisibleName(String visibleName) { 58 | this.visibleName = visibleName; 59 | } 60 | 61 | public boolean isReadOnly() { 62 | return readOnly; 63 | } 64 | public void setReadOnly(boolean readOnly) { 65 | this.readOnly = readOnly; 66 | } 67 | 68 | public int getOrder() { 69 | return order; 70 | } 71 | 72 | public void setOrder(int order) { 73 | this.order = order; 74 | } 75 | 76 | public boolean isVisible() { 77 | return visible; 78 | } 79 | public void setVisible(boolean visible) { 80 | this.visible = visible; 81 | } 82 | public String getDescription() { 83 | return description; 84 | } 85 | 86 | public String getDefaultVisibleName() { 87 | return defaultVisibleName; 88 | } 89 | 90 | 91 | @Override 92 | public Object getHeaderValue() { 93 | return this.getVisibleName(); 94 | } 95 | 96 | @Override 97 | public int compareTo(LogTableColumn logTableColumn) { 98 | return Integer.compare(this.order, logTableColumn.order); 99 | } 100 | 101 | public static class ColumnSerializer implements JsonDeserializer, JsonSerializer { 102 | //id, name, enabled, defaultVisibleName, visibleName, width, type, readonly, order, visible, description, isRegEx, regExData 103 | @Override 104 | public JsonElement serialize(LogTableColumn column, Type type, JsonSerializationContext jsonSerializationContext) { 105 | JsonObject object = new JsonObject(); 106 | object.addProperty("id", String.valueOf(column.identifier)); 107 | object.addProperty("order", column.order); 108 | object.addProperty("name", column.name); 109 | object.addProperty("defaultVisibleName", column.defaultVisibleName); 110 | object.addProperty("visibleName", column.visibleName); 111 | object.addProperty("preferredWidth", column.width); 112 | object.addProperty("readonly", column.readOnly); 113 | object.addProperty("visible", column.visible); 114 | object.addProperty("description", column.description); 115 | return object; 116 | } 117 | 118 | @Override 119 | public LogTableColumn deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { 120 | LogTableColumn column = null; 121 | JsonObject object = jsonElement.getAsJsonObject(); 122 | column = new LogTableColumn(); 123 | column.identifier = LogEntryField.getByFullyQualifiedName(object.get("id").getAsString()); 124 | column.name = object.get("name").getAsString(); 125 | column.order = object.get("order").getAsInt(); 126 | column.defaultVisibleName = object.get("defaultVisibleName").getAsString(); 127 | column.visibleName = object.get("visibleName").getAsString(); 128 | column.setWidth(object.get("preferredWidth").getAsInt()); 129 | column.readOnly = object.get("readonly").getAsBoolean(); 130 | column.visible = object.get("visible").getAsBoolean(); 131 | column.description = object.get("description").getAsString(); 132 | 133 | return column; 134 | } 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | return "LogTableColumn[" + identifier + "]"; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/logtable/LogTableController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.logtable; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 5 | import com.nccgroup.loggerplusplus.logview.LogViewController; 6 | import com.nccgroup.loggerplusplus.util.Globals; 7 | 8 | public class LogTableController { 9 | 10 | private final LogViewController logViewController; 11 | private final FilterLibraryController filterLibraryController; 12 | private final Preferences preferences; 13 | private final LogTableModel logTableModel; 14 | private final LogTableColumnModel logTableColumnModel; 15 | private final TableHeader tableHeader; 16 | private final LogTable logTable; 17 | 18 | public LogTableController(LogViewController logViewController, FilterLibraryController filterLibraryController){ 19 | this.logViewController = logViewController; 20 | this.filterLibraryController = filterLibraryController; 21 | this.preferences = logViewController.getPreferences(); 22 | 23 | this.logTableColumnModel = new LogTableColumnModel(this); 24 | this.logTableModel = new LogTableModel(this, logTableColumnModel); 25 | this.tableHeader = new TableHeader(this); 26 | this.logTable = new LogTable(this); 27 | 28 | this.filterLibraryController.addColorFilterListener(logTableModel); 29 | this.filterLibraryController.addTagListener(logTableModel); 30 | } 31 | 32 | public LogViewController getLogViewController() { 33 | return logViewController; 34 | } 35 | 36 | public LogTableColumnModel getLogTableColumnModel() { 37 | return logTableColumnModel; 38 | } 39 | 40 | public LogTableModel getLogTableModel() { 41 | return logTableModel; 42 | } 43 | 44 | public LogTable getLogTable() { 45 | return logTable; 46 | } 47 | 48 | public TableHeader getTableHeader() { 49 | return tableHeader; 50 | } 51 | 52 | public Preferences getPreferences() { 53 | return preferences; 54 | } 55 | 56 | 57 | public void reset(){ 58 | logTableModel.reset(); 59 | } 60 | 61 | public int getMaximumEntries(){ 62 | return preferences.getSetting(Globals.PREF_MAXIMUM_ENTRIES); 63 | } 64 | 65 | public void reinitialize(){ 66 | //TODO Reinitialize table model 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/logtable/TableHeader.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.logtable; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.JTableHeader; 5 | import java.awt.*; 6 | import java.awt.event.MouseAdapter; 7 | import java.awt.event.MouseEvent; 8 | 9 | // This was used to create tool tips 10 | public class TableHeader extends JTableHeader { 11 | 12 | 13 | TableHeader(LogTableController logTableController) { 14 | super(logTableController.getLogTableColumnModel()); 15 | 16 | this.setTable(logTableController.getLogTable()); 17 | 18 | this.addMouseListener(new MouseAdapter(){ 19 | @Override 20 | public void mouseReleased(MouseEvent e) 21 | { 22 | if ( SwingUtilities.isRightMouseButton( e )){ 23 | // get the coordinates of the mouse click 24 | Point p = e.getPoint(); 25 | int columnIndex = columnAtPoint(p); 26 | LogTableColumn column = (LogTableColumn) getColumnModel().getColumn(columnIndex); 27 | 28 | TableHeaderMenu tblHeaderMenu = new TableHeaderMenu(logTableController, column); 29 | tblHeaderMenu.showMenu(e); 30 | } 31 | } 32 | }); 33 | 34 | } 35 | 36 | @Override 37 | public String getToolTipText(MouseEvent e) { 38 | 39 | // get the coordinates of the mouse click 40 | Point p = e.getPoint(); 41 | int columnID = TableHeader.this.getTable().convertColumnIndexToModel(TableHeader.this.getTable().columnAtPoint(p)); 42 | LogTableColumn column = (LogTableColumn) TableHeader.this.getTable().getColumnModel().getColumn(columnID); 43 | 44 | String retStr; 45 | try { 46 | retStr = column.getDescription(); 47 | } catch (NullPointerException | ArrayIndexOutOfBoundsException ex) { 48 | retStr = ""; 49 | } 50 | if (retStr.length() < 1) { 51 | retStr = super.getToolTipText(e); 52 | } 53 | return retStr; 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/logtable/TableHeaderMenu.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.logtable; 2 | 3 | import com.nccgroup.loggerplusplus.logentry.FieldGroup; 4 | import com.nccgroup.loggerplusplus.util.MoreHelp; 5 | 6 | import javax.swing.*; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.MouseEvent; 10 | import java.util.*; 11 | 12 | 13 | public class TableHeaderMenu extends JPopupMenu{ 14 | 15 | private final LogTableController logTableController; 16 | private final LogTable logTable; 17 | private final LogTableColumn columnObj; 18 | 19 | public TableHeaderMenu(LogTableController logTableController, LogTableColumn columnObj) 20 | { 21 | this.logTableController = logTableController; 22 | this.logTable = logTableController.getLogTable(); 23 | this.columnObj=columnObj; 24 | 25 | } 26 | 27 | public void showMenu(MouseEvent e){ 28 | 29 | JPopupMenu menu = new JPopupMenu("Popup"); 30 | JMenuItem item = new JMenuItem(columnObj.getVisibleName() + " (" + columnObj.getIdentifier().getFullLabel() + ")"); 31 | 32 | item.setEnabled(false); 33 | menu.add(item); 34 | menu.addSeparator(); 35 | 36 | item = new JMenuItem("Rename"); 37 | item.addActionListener(new ActionListener() { 38 | public void actionPerformed(ActionEvent e) { 39 | String newValue = MoreHelp.showPlainInputMessage("Rename the \"" + columnObj.getDefaultVisibleName()+ 40 | "\" column", "Rename column name", columnObj.getVisibleName()); 41 | if(newValue.isEmpty()){ 42 | newValue = columnObj.getDefaultVisibleName(); 43 | } 44 | // Save it only if it is different! no need to refresh the columns 45 | if(!newValue.equals(columnObj.getDefaultVisibleName())){ 46 | columnObj.setVisibleName(newValue); 47 | logTableController.getLogTableColumnModel().saveLayout(); 48 | } 49 | } 50 | }); 51 | menu.add(item); 52 | 53 | item = new JMenuItem("Hide"); 54 | item.addActionListener(new ActionListener() { 55 | public void actionPerformed(ActionEvent e) { 56 | logTable.getColumnModel().toggleHidden(columnObj); 57 | } 58 | }); 59 | menu.add(item); 60 | 61 | JMenu subMenuVisibleCols = new JMenu("Visible columns"); 62 | item = new JMenuItem("Make all visible"); 63 | item.addActionListener(new ActionListener() { 64 | public void actionPerformed(ActionEvent e) { 65 | for (LogTableColumn column : logTable.getColumnModel().getAllColumns()) { 66 | logTable.getColumnModel().showColumn(column); 67 | } 68 | logTableController.getLogTableColumnModel().saveLayout(); 69 | } 70 | }); 71 | subMenuVisibleCols.add(item); 72 | 73 | Map groupMenus = new HashMap<>(); 74 | 75 | for (LogTableColumn logTableColumn : logTable.getColumnModel().getAllColumns()) { 76 | 77 | FieldGroup group = logTableColumn.getIdentifier().getFieldGroup(); 78 | if (!groupMenus.containsKey(group)) { 79 | groupMenus.put(group, new JMenu(group.getLabel())); 80 | } 81 | JMenu fieldGroupMenu = groupMenus.get(group); 82 | 83 | JMenuItem visibleItem = new JCheckBoxMenuItem(logTableColumn.getVisibleName()); 84 | visibleItem.setSelected(logTableColumn.isVisible()); 85 | visibleItem.addActionListener(e1 -> logTable.getColumnModel().toggleHidden(logTableColumn)); 86 | fieldGroupMenu.add(visibleItem); 87 | } 88 | 89 | List fieldGroups = new ArrayList(groupMenus.keySet()); 90 | fieldGroups.sort(Enum::compareTo); 91 | 92 | for (FieldGroup fieldGroup : fieldGroups) { 93 | subMenuVisibleCols.add(groupMenus.get(fieldGroup)); 94 | } 95 | 96 | menu.add(subMenuVisibleCols); 97 | 98 | menu.show(e.getComponent(), e.getX(), e.getY()); 99 | } 100 | 101 | 102 | 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/processor/EntryImportWorker.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.processor; 2 | 3 | import burp.api.montoya.core.ToolType; 4 | import burp.api.montoya.http.message.HttpRequestResponse; 5 | import burp.api.montoya.http.message.requests.HttpRequest; 6 | import burp.api.montoya.http.message.responses.HttpResponse; 7 | import burp.api.montoya.proxy.ProxyHttpRequestResponse; 8 | import com.nccgroup.loggerplusplus.logentry.LogEntry; 9 | 10 | import javax.swing.*; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.CountDownLatch; 14 | import java.util.concurrent.ThreadPoolExecutor; 15 | import java.util.function.Consumer; 16 | 17 | public class EntryImportWorker extends SwingWorker { 18 | 19 | private final LogProcessor logProcessor; 20 | private final ToolType originatingTool; 21 | private final List proxyEntries; 22 | private final List httpEntries; 23 | private final Consumer> interimConsumer; 24 | private final Runnable callback; 25 | private final boolean sendToAutoExporters; 26 | 27 | private EntryImportWorker(Builder builder){ 28 | this.logProcessor = builder.logProcessor; 29 | this.originatingTool = builder.originatingTool; 30 | this.proxyEntries = builder.proxyEntries; 31 | this.httpEntries = builder.httpEntries; 32 | this.interimConsumer = builder.interimConsumer; 33 | this.callback = builder.callback; 34 | this.sendToAutoExporters = builder.sendToAutoExporters; 35 | } 36 | 37 | @Override 38 | protected Void doInBackground() throws Exception { 39 | logProcessor.getEntryProcessExecutor().pause(); //Pause the processor, we don't want it mixing with our import. 40 | boolean isProxyEntries = proxyEntries.size() > 0; 41 | int count = isProxyEntries ? proxyEntries.size() : httpEntries.size(); 42 | 43 | 44 | CountDownLatch countDownLatch = new CountDownLatch(count); 45 | ThreadPoolExecutor entryImportExecutor = logProcessor.getEntryImportExecutor(); 46 | for (int index = 0; index < count; index++) { 47 | if(entryImportExecutor.isShutdown() || this.isCancelled()) return null; 48 | HttpRequest request; 49 | HttpResponse response; 50 | if(isProxyEntries){ 51 | request = proxyEntries.get(index).finalRequest(); 52 | response = proxyEntries.get(index).originalResponse(); 53 | }else{ 54 | request = httpEntries.get(index).request(); 55 | response = httpEntries.get(index).response(); 56 | } 57 | final LogEntry logEntry = new LogEntry(originatingTool, request, response); 58 | int finalIndex = index; 59 | entryImportExecutor.submit(() -> { 60 | if(this.isCancelled()) return; 61 | LogEntry result = logProcessor.processEntry(logEntry); 62 | if(result != null) { 63 | logProcessor.addNewEntry(logEntry, sendToAutoExporters); 64 | } 65 | publish(finalIndex); 66 | countDownLatch.countDown(); 67 | }); 68 | } 69 | countDownLatch.await(); 70 | return null; 71 | } 72 | 73 | @Override 74 | protected void process(List chunks) { 75 | if(this.interimConsumer != null) 76 | interimConsumer.accept(chunks); 77 | } 78 | 79 | @Override 80 | protected void done() { 81 | logProcessor.getEntryProcessExecutor().resume(); 82 | if(this.callback != null) callback.run(); 83 | super.done(); 84 | } 85 | 86 | public static class Builder { 87 | 88 | private final LogProcessor logProcessor; 89 | private ToolType originatingTool = ToolType.EXTENSIONS; 90 | private List proxyEntries = new ArrayList<>(); 91 | private List httpEntries = new ArrayList<>(); 92 | private Consumer> interimConsumer; 93 | private Runnable callback; 94 | private boolean sendToAutoExporters = false; 95 | 96 | Builder(LogProcessor logProcessor){ 97 | this.logProcessor = logProcessor; 98 | } 99 | 100 | public Builder setOriginatingTool(ToolType originatingTool){ 101 | this.originatingTool = originatingTool; 102 | return this; 103 | } 104 | 105 | public Builder setProxyEntries(List entries) { 106 | this.proxyEntries.addAll(entries); 107 | this.httpEntries.clear(); 108 | return this; 109 | } 110 | 111 | public Builder setHttpEntries(List entries) { 112 | this.httpEntries.addAll(entries); 113 | this.proxyEntries.clear(); 114 | return this; 115 | } 116 | 117 | public Builder setInterimConsumer(Consumer> interimConsumer) { 118 | this.interimConsumer = interimConsumer; 119 | return this; 120 | } 121 | 122 | public Builder setCallback(Runnable callback) { 123 | this.callback = callback; 124 | return this; 125 | } 126 | 127 | //Control if the imported entries should also be sent to exporters (e.g. ElasticSearch) 128 | //Prevents existing entries being re-exported 129 | public Builder setSendToAutoExporters(boolean autoExport) { 130 | this.sendToAutoExporters = autoExport; 131 | return this; 132 | } 133 | 134 | public EntryImportWorker build() { 135 | return new EntryImportWorker(this); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/logview/processor/LogProcessorHelper.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.logview.processor; 2 | 3 | import burp.api.montoya.core.Annotations; 4 | import com.nccgroup.loggerplusplus.util.Globals; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import java.util.regex.Matcher; 8 | 9 | public class LogProcessorHelper { 10 | 11 | public static Annotations addIdentifierInComment(Integer identifier, Annotations annotations) { 12 | String originalComment = annotations.notes() != null ? annotations.notes() : ""; 13 | annotations = annotations.withNotes(originalComment + "$LPP:" + identifier + "$"); 14 | return annotations; 15 | } 16 | 17 | public static Object[] extractAndRemoveIdentifierFromRequestResponseComment(Annotations annotations) { 18 | Integer identifier = null; 19 | if (!StringUtils.isEmpty(annotations.notes())) { 20 | Matcher matcher = Globals.LOG_ENTRY_ID_PATTERN.matcher(annotations.notes()); 21 | if (matcher.find()) { 22 | identifier = Integer.parseInt(matcher.group(1)); 23 | annotations = annotations.withNotes(matcher.replaceAll("")); 24 | } 25 | } 26 | 27 | return new Object[]{identifier,annotations}; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/preferences/PreferencesController.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.preferences; 2 | 3 | import burp.api.montoya.MontoyaApi; 4 | import com.coreyd97.BurpExtenderUtilities.IGsonProvider; 5 | import com.coreyd97.BurpExtenderUtilities.ILogProvider; 6 | import com.coreyd97.BurpExtenderUtilities.Preferences; 7 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 8 | import lombok.Getter; 9 | import lombok.extern.log4j.Log4j2; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | @Log4j2 14 | public class PreferencesController { 15 | 16 | @Getter 17 | private final IGsonProvider gsonProvider; 18 | 19 | @Getter 20 | private final Preferences preferences; 21 | 22 | private PreferencesPanel preferencesPanel; 23 | 24 | public PreferencesController(MontoyaApi montoya) { 25 | this.gsonProvider = LoggerPlusPlus.gsonProvider; 26 | this.preferences = new LoggerPreferenceFactory(montoya, 27 | gsonProvider, 28 | new ILogProvider() { 29 | @Override 30 | public void logOutput(String message) { 31 | log.debug(message); 32 | } 33 | 34 | @Override 35 | public void logError(String errorMessage) { 36 | log.error(errorMessage); 37 | } 38 | } 39 | ).buildPreferences(); 40 | } 41 | 42 | public PreferencesPanel getPreferencesPanel() { 43 | if(this.preferencesPanel == null) { 44 | this.preferencesPanel = new PreferencesPanel(this); 45 | } 46 | 47 | return preferencesPanel; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/filter/BlacklistFilter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.filter; 2 | 3 | import burp.api.montoya.http.message.params.HttpParameter; 4 | import com.coreyd97.BurpExtenderUtilities.Preferences; 5 | import com.google.gson.reflect.TypeToken; 6 | import com.nccgroup.loggerplusplus.util.MoreHelp; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.TreeSet; 13 | 14 | public class BlacklistFilter extends ParameterFilter { 15 | 16 | private static String BLACKLIST_PREF = "parameterValueBlacklist"; 17 | private static HashSet defaultBlacklist = new HashSet<>(Arrays.asList("0","1","true","false")); 18 | 19 | private final Set blacklist = new TreeSet(String.CASE_INSENSITIVE_ORDER); 20 | 21 | public BlacklistFilter(Preferences preferences){ 22 | super(preferences, "Value Blacklist"); 23 | preferences.registerSetting(BLACKLIST_PREF, new TypeToken>(){}.getType(), defaultBlacklist); 24 | 25 | blacklist.addAll(preferences.getSetting(BLACKLIST_PREF)); 26 | } 27 | 28 | @Override 29 | public boolean isFiltered(HttpParameter parameter) { 30 | return blacklist.contains(parameter.value()); 31 | } 32 | 33 | @Override 34 | public void showConfigDialog() { 35 | String valueString = MoreHelp.showPlainInputMessage("Enter comma separated blacklist values:", "Parameter Value Blacklist", StringUtils.join(blacklist, ",")); 36 | String[] values = valueString.split(","); 37 | blacklist.clear(); 38 | for (String value : values) { 39 | if(!value.isEmpty()) { 40 | blacklist.add(value.trim()); 41 | } 42 | } 43 | preferences.setSetting(BLACKLIST_PREF, blacklist); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/filter/LengthFilter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.filter; 2 | 3 | import burp.api.montoya.http.message.params.HttpParameter; 4 | import com.coreyd97.BurpExtenderUtilities.Alignment; 5 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 6 | import com.coreyd97.BurpExtenderUtilities.Preferences; 7 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 8 | 9 | import javax.swing.*; 10 | 11 | public class LengthFilter extends ParameterFilter { 12 | 13 | private static String LENGTH_MIN_PREF = "lengthMinFilter"; 14 | private static String LENGTH_MAX_PREF = "lengthMaxFilter"; 15 | private int min_length; 16 | private int max_length; 17 | 18 | public LengthFilter(Preferences preferences){ 19 | super(preferences, "Value Length Range"); 20 | preferences.registerSetting(LENGTH_MAX_PREF, int.class, 999); 21 | preferences.registerSetting(LENGTH_MIN_PREF, int.class, 3); 22 | 23 | min_length = preferences.getSetting(LENGTH_MIN_PREF); 24 | max_length = preferences.getSetting(LENGTH_MAX_PREF); 25 | } 26 | 27 | @Override 28 | public boolean isFiltered(HttpParameter parameter) { 29 | int len = parameter.value().length(); 30 | return len < min_length || len > max_length; 31 | 32 | } 33 | 34 | @Override 35 | public void showConfigDialog() { 36 | JLabel info = new JLabel("Enter parameter value length range:"); 37 | JSpinner minLengthSpinner = new JSpinner(new SpinnerNumberModel(min_length, 0, 99999, 1)); 38 | JSpinner maxLengthSpinner = new JSpinner(new SpinnerNumberModel(max_length, 0, 99999, 1)); 39 | JPanel panel = PanelBuilder.build(new JComponent[][]{ 40 | new JComponent[]{info, info}, 41 | new JComponent[]{new JLabel("Minimum: "), minLengthSpinner}, 42 | new JComponent[]{new JLabel("Maximum: "), maxLengthSpinner} 43 | }, new int[][]{ 44 | new int[]{0}, 45 | new int[]{1}, 46 | new int[]{1}, 47 | }, Alignment.FILL, 1.0, 1.0); 48 | int result = JOptionPane.showConfirmDialog(LoggerPlusPlus.instance.getLoggerFrame(), panel, "Reflection Value Length Filter", JOptionPane.OK_CANCEL_OPTION); 49 | if(result == JOptionPane.OK_OPTION){ 50 | min_length = (int) minLengthSpinner.getValue(); 51 | max_length = (int) maxLengthSpinner.getValue(); 52 | preferences.setSetting(LENGTH_MIN_PREF, min_length); 53 | preferences.setSetting(LENGTH_MAX_PREF, max_length); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/filter/ParameterFilter.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.filter; 2 | 3 | import burp.api.montoya.http.message.params.HttpParameter; 4 | import com.coreyd97.BurpExtenderUtilities.Preferences; 5 | 6 | public abstract class ParameterFilter { 7 | 8 | protected final Preferences preferences; 9 | private final String name; 10 | private final String enabledPrefKey; 11 | private boolean enabled; 12 | 13 | ParameterFilter(Preferences preferences, String name){ 14 | this.preferences = preferences; 15 | this.name = name; 16 | this.enabledPrefKey = "ParamFilter_" + name + "_Enabled"; 17 | preferences.registerSetting(enabledPrefKey, boolean.class, true); 18 | this.enabled = preferences.getSetting(enabledPrefKey); 19 | } 20 | 21 | public String getName(){ 22 | return this.name; 23 | }; 24 | 25 | public void setEnabled(boolean enabled){ 26 | this.enabled = enabled; 27 | preferences.setSetting(enabledPrefKey, enabled); 28 | } 29 | 30 | public boolean isEnabled() { 31 | return enabled; 32 | } 33 | 34 | public abstract boolean isFiltered(HttpParameter parameter); 35 | public abstract void showConfigDialog(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/Base64DecodeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.util.Base64; 7 | 8 | public class Base64DecodeTransformer extends ParameterValueTransformer { 9 | 10 | public Base64DecodeTransformer(Preferences preferences){ 11 | super(preferences, "Base64 Decode"); 12 | } 13 | 14 | @Override 15 | public String transform(String string) throws UnsupportedEncodingException { 16 | return new String(Base64.getDecoder().decode(string.getBytes())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/Base64EncodeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.util.Base64; 7 | 8 | public class Base64EncodeTransformer extends ParameterValueTransformer { 9 | 10 | public Base64EncodeTransformer(Preferences preferences){ 11 | super(preferences, "Base64 Encode"); 12 | } 13 | 14 | @Override 15 | public String transform(String string) throws UnsupportedEncodingException { 16 | return new String(Base64.getEncoder().encode(string.getBytes())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/HTMLEscapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class HTMLEscapeTransformer extends ParameterValueTransformer { 7 | 8 | public HTMLEscapeTransformer(Preferences preferences){ 9 | super(preferences, "HTML Escape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.escapeHtml4(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/HTMLUnescapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class HTMLUnescapeTransformer extends ParameterValueTransformer { 7 | 8 | public HTMLUnescapeTransformer(Preferences preferences){ 9 | super(preferences, "HTML Unescape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.unescapeHtml4(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/HexEncodeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.codec.binary.Hex; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | 8 | public class HexEncodeTransformer extends ParameterValueTransformer { 9 | 10 | public HexEncodeTransformer(Preferences preferences){ 11 | super(preferences, "Hex Encode"); 12 | } 13 | 14 | @Override 15 | public String transform(String string) throws UnsupportedEncodingException { 16 | return Hex.encodeHexString(string.getBytes()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/JsonEscapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class JsonEscapeTransformer extends ParameterValueTransformer { 7 | 8 | public JsonEscapeTransformer(Preferences preferences){ 9 | super(preferences, "Json Escape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.escapeJson(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/JsonUnescapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class JsonUnescapeTransformer extends ParameterValueTransformer { 7 | 8 | public JsonUnescapeTransformer(Preferences preferences){ 9 | super(preferences, "Json Unescape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.unescapeJson(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/ParameterValueTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | public abstract class ParameterValueTransformer { 6 | 7 | protected final Preferences preferences; 8 | private final String name; 9 | private final String enabledPrefKey; 10 | private boolean enabled; 11 | 12 | ParameterValueTransformer(Preferences preferences, String name){ 13 | this.preferences = preferences; 14 | this.name = name; 15 | this.enabledPrefKey = "ParamTransformer_" + name + "_Enabled"; 16 | preferences.registerSetting(enabledPrefKey, boolean.class, true); 17 | this.enabled = preferences.getSetting(enabledPrefKey); 18 | } 19 | 20 | public String getName(){ 21 | return this.name; 22 | }; 23 | 24 | public void setEnabled(boolean enabled){ 25 | this.enabled = enabled; 26 | preferences.setSetting(enabledPrefKey, enabled); 27 | } 28 | 29 | public boolean isEnabled() { 30 | return enabled; 31 | } 32 | 33 | public abstract String transform(String value) throws Exception; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/URLDecodeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLDecoder; 7 | 8 | public class URLDecodeTransformer extends ParameterValueTransformer { 9 | 10 | public URLDecodeTransformer(Preferences preferences){ 11 | super(preferences, "URL Decode"); 12 | } 13 | 14 | @Override 15 | public String transform(String string) throws UnsupportedEncodingException { 16 | return URLDecoder.decode(string, "UTF-8"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/URLEncodeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLEncoder; 7 | 8 | public class URLEncodeTransformer extends ParameterValueTransformer { 9 | 10 | public URLEncodeTransformer(Preferences preferences){ 11 | super(preferences, "URL Encode"); 12 | } 13 | 14 | @Override 15 | public String transform(String string) throws UnsupportedEncodingException { 16 | return URLEncoder.encode(string, "UTF-8"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/XMLEscapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class XMLEscapeTransformer extends ParameterValueTransformer { 7 | 8 | public XMLEscapeTransformer(Preferences preferences){ 9 | super(preferences, "XML Escape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.escapeXml11(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/reflection/transformer/XMLUnescapeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.reflection.transformer; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Preferences; 4 | import org.apache.commons.text.StringEscapeUtils; 5 | 6 | public class XMLUnescapeTransformer extends ParameterValueTransformer { 7 | 8 | public XMLUnescapeTransformer(Preferences preferences){ 9 | super(preferences, "XML Unescape"); 10 | } 11 | 12 | @Override 13 | public String transform(String string) { 14 | return StringEscapeUtils.unescapeXml(string); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class NamedThreadFactory implements ThreadFactory { 7 | 8 | private final String name; 9 | private final AtomicInteger atomicInteger; 10 | 11 | public NamedThreadFactory(String name){ 12 | this.name = name; 13 | this.atomicInteger = new AtomicInteger(0); 14 | } 15 | 16 | @Override 17 | public Thread newThread(Runnable r) { 18 | return new Thread(r, String.format("%s-Thread-%d", this.name, this.atomicInteger.incrementAndGet())); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/PausableThreadPoolExecutor.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util; 2 | 3 | import java.util.concurrent.*; 4 | import java.util.concurrent.locks.Condition; 5 | import java.util.concurrent.locks.ReentrantLock; 6 | 7 | public class PausableThreadPoolExecutor extends ThreadPoolExecutor { 8 | private boolean isPaused; 9 | private ReentrantLock pauseLock = new ReentrantLock(); 10 | private Condition unpaused = pauseLock.newCondition(); 11 | 12 | public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { 13 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); 14 | } 15 | 16 | public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) { 17 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); 18 | } 19 | 20 | public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) { 21 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); 22 | } 23 | 24 | public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { 25 | super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); 26 | } 27 | 28 | protected void beforeExecute(Thread t, Runnable r) { 29 | super.beforeExecute(t, r); 30 | pauseLock.lock(); 31 | try { 32 | while (isPaused) unpaused.await(); 33 | } catch(InterruptedException ie) { 34 | t.interrupt(); 35 | } finally { 36 | pauseLock.unlock(); 37 | } 38 | } 39 | 40 | public void pause() { 41 | pauseLock.lock(); 42 | try { 43 | isPaused = true; 44 | } finally { 45 | pauseLock.unlock(); 46 | } 47 | } 48 | 49 | public void resume() { 50 | pauseLock.lock(); 51 | try { 52 | isPaused = false; 53 | unpaused.signalAll(); 54 | } finally { 55 | pauseLock.unlock(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/SwingWorkerWithProgressDialog.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util; 2 | 3 | import com.coreyd97.BurpExtenderUtilities.Alignment; 4 | import com.coreyd97.BurpExtenderUtilities.PanelBuilder; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.ActionEvent; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public abstract class SwingWorkerWithProgressDialog extends SwingWorker { 13 | 14 | private final JProgressBar jProgressBar; 15 | private final JDialog dialog; 16 | 17 | public SwingWorkerWithProgressDialog(Frame dialogOwner, String title, String message, int progressBarMax){ 18 | jProgressBar = new JProgressBar(0, progressBarMax); 19 | dialog = new JDialog(dialogOwner, title, false); 20 | JLabel messageLabel = new JLabel(message); 21 | JButton cancelButton = new JButton(new AbstractAction("Cancel") { 22 | @Override 23 | public void actionPerformed(ActionEvent actionEvent) { 24 | SwingWorkerWithProgressDialog.this.cancel(true); 25 | } 26 | }); 27 | 28 | JPanel bodyPanel = PanelBuilder.build(new Component[][]{ 29 | new Component[]{messageLabel, messageLabel}, 30 | new Component[]{jProgressBar, cancelButton} 31 | }, new int[][]{ 32 | new int[]{0, 0}, 33 | new int[]{1, 0} 34 | }, Alignment.CENTER, 0.8, 0.8); 35 | 36 | dialog.add(bodyPanel); 37 | dialog.setResizable(false); 38 | dialog.pack(); 39 | 40 | dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 41 | } 42 | 43 | @Override 44 | protected T doInBackground() throws Exception { 45 | dialog.setVisible(true); 46 | return null; 47 | } 48 | 49 | @Override 50 | protected void process(List chunks) { 51 | jProgressBar.setValue(Collections.max(chunks)); 52 | } 53 | 54 | @Override 55 | protected void done() { 56 | dialog.dispose(); 57 | super.done(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/ColorEditor.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.TableCellEditor; 5 | import java.awt.*; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | //https://docs.oracle.com/javase/tutorial/uiswing/components/table.html 9 | public class ColorEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { 10 | Color currentColor; 11 | JButton button; 12 | JColorChooser colorChooser; 13 | JDialog dialog; 14 | protected static final String EDIT = "edit"; 15 | 16 | public ColorEditor() { 17 | button = new JButton(); 18 | button.setActionCommand(EDIT); 19 | button.addActionListener(this); 20 | button.setBorderPainted(false); 21 | 22 | //Set up the dialog that the button brings up. 23 | colorChooser = new JColorChooser(); 24 | dialog = JColorChooser.createDialog(button, 25 | "Pick a Color", 26 | true, //modal 27 | colorChooser, 28 | this, //OK button handler 29 | null); //no CANCEL button handler 30 | } 31 | 32 | public void actionPerformed(ActionEvent e) { 33 | if (EDIT.equals(e.getActionCommand())) { 34 | //The user has clicked the cell, so 35 | //bring up the dialog. 36 | button.setBackground(currentColor); 37 | colorChooser.setColor(currentColor); 38 | dialog.setVisible(true); 39 | 40 | fireEditingStopped(); //Make the renderer reappear. 41 | 42 | } else { //User pressed dialog's "OK" button. 43 | currentColor = colorChooser.getColor(); 44 | } 45 | } 46 | 47 | //Implement the one CellEditor method that AbstractCellEditor doesn't. 48 | public Object getCellEditorValue() { 49 | return currentColor; 50 | } 51 | 52 | //Implement the one method defined by TableCellEditor. 53 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 54 | currentColor = (Color)value; 55 | return button; 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/NoTextSelectionCaret.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface; 2 | 3 | import javax.swing.text.DefaultCaret; 4 | import javax.swing.text.JTextComponent; 5 | 6 | public class NoTextSelectionCaret extends DefaultCaret { 7 | public NoTextSelectionCaret(JTextComponent component){ 8 | setBlinkRate(component.getCaret().getBlinkRate()); 9 | component.setHighlighter(null); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/WrappedTextPane.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface; 2 | 3 | import javax.swing.*; 4 | import javax.swing.text.*; 5 | 6 | public class WrappedTextPane extends JTextPane { 7 | 8 | public WrappedTextPane(StyledDocument styledDocument){ 9 | this.setEditorKit(new WrapEditorKit()); 10 | } 11 | 12 | public WrappedTextPane(){ 13 | this.setEditorKit(new WrapEditorKit()); 14 | } 15 | 16 | class WrapEditorKit extends StyledEditorKit { 17 | ViewFactory factory = new WrapColumnFactory(); 18 | public ViewFactory getViewFactory() { 19 | return factory; 20 | } 21 | } 22 | 23 | class WrapColumnFactory implements ViewFactory { 24 | public View create(Element elem) { 25 | String kind = elem.getName(); 26 | if (kind != null) { 27 | if (kind.equals(AbstractDocument.ContentElementName)) { 28 | return new WrapLabelView(elem); 29 | } else if (kind.equals(AbstractDocument.ParagraphElementName)) { 30 | return new ParagraphView(elem); 31 | } else if (kind.equals(AbstractDocument.SectionElementName)) { 32 | return new BoxView(elem, View.Y_AXIS); 33 | } else if (kind.equals(StyleConstants.ComponentElementName)) { 34 | return new ComponentView(elem); 35 | } else if (kind.equals(StyleConstants.IconElementName)) { 36 | return new IconView(elem); 37 | } 38 | } 39 | 40 | // default to text display 41 | return new LabelView(elem); 42 | } 43 | } 44 | 45 | class WrapLabelView extends LabelView { 46 | public WrapLabelView(Element elem) { 47 | super(elem); 48 | } 49 | 50 | public float getMinimumSpan(int axis) { 51 | switch (axis) { 52 | case View.X_AXIS: 53 | return 0; 54 | case View.Y_AXIS: 55 | return super.getMinimumSpan(axis); 56 | default: 57 | throw new IllegalArgumentException("Invalid axis: " + axis); 58 | } 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/dialog/ColorFilterDialog.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.dialog; 2 | 3 | import com.nccgroup.loggerplusplus.filter.colorfilter.TableColorRule; 4 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.awt.event.WindowEvent; 11 | 12 | /** 13 | * Created by corey on 19/07/17. 14 | */ 15 | public class ColorFilterDialog extends JFrame { 16 | private static ColorFilterDialog instance; 17 | private final FilterLibraryController filterLibraryController; 18 | private final ColorFilterTable filterTable; 19 | 20 | public ColorFilterDialog(FilterLibraryController filterLibraryController){ 21 | if(instance != null) instance.dispose(); 22 | instance = this; 23 | this.filterLibraryController = filterLibraryController; 24 | this.filterTable = new ColorFilterTable(filterLibraryController); 25 | buildDialog(); 26 | pack(); 27 | } 28 | 29 | private void buildDialog(){ 30 | this.setLayout(new BorderLayout()); 31 | this.setTitle("Color Filters"); 32 | JPanel content = new JPanel(new GridBagLayout()); 33 | this.add(content, BorderLayout.CENTER); 34 | final JScrollPane filterListWrapper = new JScrollPane(filterTable); 35 | GridBagConstraints gbcFilterWrapper = new GridBagConstraints(); 36 | gbcFilterWrapper.gridx = 0; 37 | gbcFilterWrapper.gridy = 0; 38 | gbcFilterWrapper.weighty = 999; 39 | gbcFilterWrapper.weightx = 999; 40 | gbcFilterWrapper.fill = GridBagConstraints.BOTH; 41 | this.setMinimumSize(filterTable.getMinimumSize()); 42 | content.add(filterListWrapper, gbcFilterWrapper); 43 | 44 | gbcFilterWrapper.gridx = 1; 45 | gbcFilterWrapper.weightx = 1; 46 | gbcFilterWrapper.fill = GridBagConstraints.HORIZONTAL; 47 | final JPanel priorityControls = new JPanel(new GridLayout(0,1)); 48 | priorityControls.add(new JButton(new AbstractAction("\u25B2") { 49 | @Override 50 | public void actionPerformed(ActionEvent actionEvent) { 51 | filterTable.moveSelectedUp(); 52 | } 53 | })); 54 | priorityControls.add(new JButton(new AbstractAction("\u25BC") { 55 | @Override 56 | public void actionPerformed(ActionEvent actionEvent) { 57 | filterTable.moveSelectedDown(); 58 | } 59 | })); 60 | content.add(priorityControls, gbcFilterWrapper); 61 | 62 | GridBagConstraints gbcFooter = new GridBagConstraints(); 63 | gbcFooter.gridx = 0; 64 | gbcFooter.gridy = 1; 65 | gbcFooter.fill = GridBagConstraints.BOTH; 66 | gbcFooter.weighty = gbcFooter.weightx = 1; 67 | gbcFooter.gridwidth = 2; 68 | JPanel buttonPanel = new JPanel(new BorderLayout()); 69 | JButton btnDeleteAll = new JButton("Delete All"); 70 | btnDeleteAll.addActionListener(new ActionListener() { 71 | @Override 72 | public void actionPerformed(ActionEvent actionEvent) { 73 | ((ColorFilterTableModel) filterTable.getModel()).removeAll(); 74 | } 75 | }); 76 | JPanel rightPanel = new JPanel(new BorderLayout()); 77 | JButton btnAddFilter = new JButton("Add LogFilter"); 78 | btnAddFilter.addActionListener(new ActionListener() { 79 | @Override 80 | public void actionPerformed(ActionEvent actionEvent) { 81 | ((ColorFilterTableModel) filterTable.getModel()).addFilter(new TableColorRule()); 82 | } 83 | }); 84 | JButton btnClose = new JButton("Close"); 85 | btnClose.addActionListener(new ActionListener() { 86 | @Override 87 | public void actionPerformed(ActionEvent actionEvent) { 88 | ColorFilterDialog.this.dispatchEvent(new WindowEvent(ColorFilterDialog.this, WindowEvent.WINDOW_CLOSING)); 89 | } 90 | }); 91 | rightPanel.add(btnAddFilter, BorderLayout.WEST); 92 | rightPanel.add(btnClose, BorderLayout.EAST); 93 | buttonPanel.add(btnDeleteAll, BorderLayout.WEST); 94 | buttonPanel.add(rightPanel, BorderLayout.EAST); 95 | content.add(buttonPanel, gbcFooter); 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/dialog/ColorFilterTable.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.dialog; 2 | 3 | import com.nccgroup.loggerplusplus.filter.colorfilter.TableColorRule; 4 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 5 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.ButtonRenderer; 6 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.ColorRenderer; 7 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.FilterRenderer; 8 | import com.nccgroup.loggerplusplus.util.userinterface.ColorEditor; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.MouseAdapter; 13 | import java.awt.event.MouseEvent; 14 | 15 | /** 16 | * Created by corey on 19/07/17. 17 | */ 18 | public class ColorFilterTable extends JTable { 19 | private final FilterLibraryController filterLibraryController; 20 | 21 | ColorFilterTable(FilterLibraryController filterLibraryController){ 22 | this.filterLibraryController = filterLibraryController; 23 | this.setModel(new ColorFilterTableModel(filterLibraryController)); 24 | this.setFillsViewportHeight(true); 25 | this.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 26 | this.setAutoCreateRowSorter(false); 27 | this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 28 | this.setRowHeight(25); 29 | ((JComponent) this.getDefaultRenderer(Boolean.class)).setOpaque(true); // to remove the white background of the checkboxes! 30 | ((JComponent) this.getDefaultRenderer(JButton.class)).setOpaque(true); 31 | 32 | this.getColumnModel().getColumn(1).setCellRenderer(new FilterRenderer()); 33 | this.getColumnModel().getColumn(2).setCellRenderer(new ColorRenderer(true)); 34 | this.getColumnModel().getColumn(2).setCellEditor(new ColorEditor()); 35 | this.getColumnModel().getColumn(3).setCellRenderer(new ColorRenderer(true)); 36 | this.getColumnModel().getColumn(3).setCellEditor(new ColorEditor()); 37 | this.getColumnModel().getColumn(5).setCellRenderer(new ButtonRenderer()); 38 | 39 | 40 | this.setDragEnabled(true); 41 | this.setDropMode(DropMode.INSERT); 42 | 43 | int[] minWidths = {100, 250, 50, 50, 100, 100}; 44 | for(int i=0; i 0){ 68 | ((ColorFilterTableModel) this.getModel()).switchRows(this.getSelectedRow(), this.getSelectedRow()-1); 69 | this.getSelectionModel().setSelectionInterval(this.getSelectedRow()-1, this.getSelectedRow()-1); 70 | TableColorRule filter = ((ColorFilterTableModel) this.getModel()).getFilterAtRow(this.getSelectedRow()); 71 | filterLibraryController.updateColorFilter(filter); 72 | } 73 | } 74 | public void moveSelectedDown() { 75 | if(this.getSelectedRow() >= 0 && this.getSelectedRow() < this.getRowCount()){ 76 | ((ColorFilterTableModel) this.getModel()).switchRows(this.getSelectedRow(), this.getSelectedRow()+1); 77 | this.getSelectionModel().setSelectionInterval(this.getSelectedRow()+1, this.getSelectedRow()+1); 78 | TableColorRule filter = ((ColorFilterTableModel) this.getModel()).getFilterAtRow(this.getSelectedRow()); 79 | filterLibraryController.updateColorFilter(filter); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/dialog/TagDialog.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.dialog; 2 | 3 | import com.nccgroup.loggerplusplus.filter.tag.Tag; 4 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.awt.event.WindowEvent; 11 | 12 | /** 13 | * Created by corey on 19/07/17. 14 | */ 15 | public class TagDialog extends JFrame { 16 | private static TagDialog instance; 17 | private final FilterLibraryController filterLibraryController; 18 | private final TagTable filterTable; 19 | 20 | public TagDialog(FilterLibraryController filterLibraryController) { 21 | if (instance != null) instance.dispose(); 22 | instance = this; 23 | this.filterLibraryController = filterLibraryController; 24 | this.filterTable = new TagTable(filterLibraryController); 25 | buildDialog(); 26 | pack(); 27 | } 28 | 29 | private void buildDialog() { 30 | this.setLayout(new BorderLayout()); 31 | this.setTitle("Log Tags"); 32 | JPanel content = new JPanel(new GridBagLayout()); 33 | this.add(content, BorderLayout.CENTER); 34 | final JScrollPane filterListWrapper = new JScrollPane(filterTable); 35 | GridBagConstraints gbcFilterWrapper = new GridBagConstraints(); 36 | gbcFilterWrapper.gridx = 0; 37 | gbcFilterWrapper.gridy = 0; 38 | gbcFilterWrapper.weighty = 999; 39 | gbcFilterWrapper.weightx = 999; 40 | gbcFilterWrapper.fill = GridBagConstraints.BOTH; 41 | this.setMinimumSize(filterTable.getMinimumSize()); 42 | content.add(filterListWrapper, gbcFilterWrapper); 43 | 44 | gbcFilterWrapper.gridx = 1; 45 | gbcFilterWrapper.weightx = 1; 46 | gbcFilterWrapper.fill = GridBagConstraints.HORIZONTAL; 47 | final JPanel priorityControls = new JPanel(new GridLayout(0, 1)); 48 | priorityControls.add(new JButton(new AbstractAction("\u25B2") { 49 | @Override 50 | public void actionPerformed(ActionEvent actionEvent) { 51 | filterTable.moveSelectedUp(); 52 | } 53 | })); 54 | priorityControls.add(new JButton(new AbstractAction("\u25BC") { 55 | @Override 56 | public void actionPerformed(ActionEvent actionEvent) { 57 | filterTable.moveSelectedDown(); 58 | } 59 | })); 60 | content.add(priorityControls, gbcFilterWrapper); 61 | 62 | GridBagConstraints gbcFooter = new GridBagConstraints(); 63 | gbcFooter.gridx = 0; 64 | gbcFooter.gridy = 1; 65 | gbcFooter.fill = GridBagConstraints.BOTH; 66 | gbcFooter.weighty = gbcFooter.weightx = 1; 67 | gbcFooter.gridwidth = 2; 68 | JPanel buttonPanel = new JPanel(new BorderLayout()); 69 | JButton btnDeleteAll = new JButton("Delete All"); 70 | btnDeleteAll.addActionListener(new ActionListener() { 71 | @Override 72 | public void actionPerformed(ActionEvent actionEvent) { 73 | ((TagTableModel) filterTable.getModel()).removeAll(); 74 | } 75 | }); 76 | JPanel rightPanel = new JPanel(new BorderLayout()); 77 | JButton btnAddFilter = new JButton("Add Tag"); 78 | btnAddFilter.addActionListener(new ActionListener() { 79 | @Override 80 | public void actionPerformed(ActionEvent actionEvent) { 81 | ((TagTableModel) filterTable.getModel()).addTag(new Tag()); 82 | } 83 | }); 84 | JButton btnClose = new JButton("Close"); 85 | btnClose.addActionListener(new ActionListener() { 86 | @Override 87 | public void actionPerformed(ActionEvent actionEvent) { 88 | TagDialog.this.dispatchEvent(new WindowEvent(TagDialog.this, WindowEvent.WINDOW_CLOSING)); 89 | } 90 | }); 91 | rightPanel.add(btnAddFilter, BorderLayout.WEST); 92 | rightPanel.add(btnClose, BorderLayout.EAST); 93 | buttonPanel.add(btnDeleteAll, BorderLayout.WEST); 94 | buttonPanel.add(rightPanel, BorderLayout.EAST); 95 | content.add(buttonPanel, gbcFooter); 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/dialog/TagTable.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.dialog; 2 | 3 | import com.nccgroup.loggerplusplus.filter.tag.Tag; 4 | import com.nccgroup.loggerplusplus.filterlibrary.FilterLibraryController; 5 | import com.nccgroup.loggerplusplus.util.userinterface.ColorEditor; 6 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.ButtonRenderer; 7 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.ColorRenderer; 8 | import com.nccgroup.loggerplusplus.util.userinterface.renderer.FilterRenderer; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.MouseAdapter; 13 | import java.awt.event.MouseEvent; 14 | 15 | /** 16 | * Created by corey on 19/07/17. 17 | */ 18 | public class TagTable extends JTable { 19 | private final FilterLibraryController filterLibraryController; 20 | 21 | TagTable(FilterLibraryController filterLibraryController) { 22 | this.filterLibraryController = filterLibraryController; 23 | this.setModel(new TagTableModel(filterLibraryController)); 24 | this.setFillsViewportHeight(true); 25 | this.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 26 | this.setAutoCreateRowSorter(false); 27 | this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 28 | this.setRowHeight(25); 29 | ((JComponent) this.getDefaultRenderer(Boolean.class)).setOpaque(true); // to remove the white background of the checkboxes! 30 | ((JComponent) this.getDefaultRenderer(JButton.class)).setOpaque(true); 31 | 32 | this.getColumnModel().getColumn(1).setCellRenderer(new FilterRenderer()); 33 | this.getColumnModel().getColumn(2).setCellRenderer(new ColorRenderer(true)); 34 | this.getColumnModel().getColumn(2).setCellEditor(new ColorEditor()); 35 | this.getColumnModel().getColumn(3).setCellRenderer(new ColorRenderer(true)); 36 | this.getColumnModel().getColumn(3).setCellEditor(new ColorEditor()); 37 | this.getColumnModel().getColumn(5).setCellRenderer(new ButtonRenderer()); 38 | 39 | 40 | this.setDragEnabled(true); 41 | this.setDropMode(DropMode.INSERT); 42 | 43 | int[] minWidths = {100, 250, 100, 100}; 44 | for (int i = 0; i < minWidths.length; i++) { 45 | this.getColumnModel().getColumn(i).setMinWidth(minWidths[i]); 46 | } 47 | int[] maxWidths = {9999, 9999, 100, 100}; 48 | for (int i = 0; i < maxWidths.length; i++) { 49 | this.getColumnModel().getColumn(i).setMaxWidth(maxWidths[i]); 50 | } 51 | this.setMinimumSize(new Dimension(850, 200)); 52 | 53 | final JTable _this = this; 54 | this.addMouseListener(new MouseAdapter() { 55 | @Override 56 | public void mouseReleased(MouseEvent mouseEvent) { 57 | if (SwingUtilities.isLeftMouseButton(mouseEvent)) { 58 | int col = _this.columnAtPoint(mouseEvent.getPoint()); 59 | int row = _this.rowAtPoint(mouseEvent.getPoint()); 60 | ((TagTableModel) getModel()).onClick(row, col); 61 | } 62 | } 63 | }); 64 | } 65 | 66 | public void moveSelectedUp() { 67 | if (this.getSelectedRow() > 0) { 68 | ((TagTableModel) this.getModel()).switchRows(this.getSelectedRow(), this.getSelectedRow() - 1); 69 | this.getSelectionModel().setSelectionInterval(this.getSelectedRow() - 1, this.getSelectedRow() - 1); 70 | Tag filter = ((TagTableModel) this.getModel()).getTagAtRow(this.getSelectedRow()); 71 | filterLibraryController.updateTag(filter); 72 | } 73 | } 74 | 75 | public void moveSelectedDown() { 76 | if (this.getSelectedRow() >= 0 && this.getSelectedRow() < this.getRowCount()) { 77 | ((TagTableModel) this.getModel()).switchRows(this.getSelectedRow(), this.getSelectedRow() + 1); 78 | this.getSelectionModel().setSelectionInterval(this.getSelectedRow() + 1, this.getSelectedRow() + 1); 79 | Tag filter = ((TagTableModel) this.getModel()).getTagAtRow(this.getSelectedRow()); 80 | filterLibraryController.updateTag(filter); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/BooleanRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | import javax.swing.*; 4 | import javax.swing.border.Border; 5 | import javax.swing.border.EmptyBorder; 6 | import javax.swing.plaf.UIResource; 7 | import javax.swing.table.TableCellRenderer; 8 | import java.awt.*; 9 | 10 | /** 11 | * Created by corey on 07/09/17. 12 | */ 13 | public class BooleanRenderer extends JCheckBox implements TableCellRenderer, UIResource { 14 | private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); 15 | 16 | public BooleanRenderer() { 17 | this.setHorizontalAlignment(0); 18 | this.setBorderPainted(true); 19 | this.setOpaque(true); 20 | } 21 | 22 | public Component getTableCellRendererComponent(JTable var1, Object var2, boolean var3, boolean var4, int var5, int var6) { 23 | if(var3) { 24 | this.setForeground(var1.getSelectionForeground()); 25 | super.setBackground(var1.getSelectionBackground()); 26 | } else { 27 | this.setForeground(var1.getForeground()); 28 | this.setBackground(var1.getBackground()); 29 | } 30 | 31 | this.setSelected(var2 != null && ((Boolean)var2).booleanValue()); 32 | if(var4) { 33 | this.setBorder(UIManager.getBorder("LogTable.focusCellHighlightBorder")); 34 | } else { 35 | this.setBorder(noFocusBorder); 36 | } 37 | 38 | return this; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/ButtonRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.TableCellRenderer; 5 | import java.awt.*; 6 | 7 | /** 8 | * Created by corey on 22/08/17. 9 | */ 10 | public class ButtonRenderer implements TableCellRenderer { 11 | @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 12 | JButton button = (JButton)value; 13 | button.setOpaque(false); 14 | button.setForeground(table.getForeground()); 15 | button.setBackground(UIManager.getColor("Button.background")); 16 | return button; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/ColorRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | /* 4 | * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. 5 | * https://docs.oracle.com/javase/tutorial/uiswing/examples/components/TableDialogEditDemoProject/src/components/ColorRenderer.java 6 | */ 7 | 8 | import javax.swing.*; 9 | import javax.swing.border.Border; 10 | import javax.swing.table.TableCellRenderer; 11 | import java.awt.*; 12 | 13 | public class ColorRenderer extends JLabel 14 | implements TableCellRenderer { 15 | Border unselectedBorder = null; 16 | Border selectedBorder = null; 17 | boolean isBordered = true; 18 | 19 | public ColorRenderer(boolean isBordered) { 20 | this.isBordered = isBordered; 21 | setOpaque(true); 22 | } 23 | 24 | public Component getTableCellRendererComponent( 25 | JTable table, Object color, 26 | boolean isSelected, boolean hasFocus, 27 | int row, int column) { 28 | Color newColor = (Color)color; 29 | setBackground(newColor); 30 | if (isBordered) { 31 | if (isSelected) { 32 | if (selectedBorder == null) { 33 | selectedBorder = BorderFactory.createMatteBorder(2,5,2,5, 34 | table.getSelectionBackground()); 35 | } 36 | setBorder(selectedBorder); 37 | } else { 38 | if (unselectedBorder == null) { 39 | unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5, 40 | table.getBackground()); 41 | } 42 | setBorder(unselectedBorder); 43 | } 44 | } 45 | if(newColor != null) { 46 | setToolTipText("RGB value: " + newColor.getRed() + ", " 47 | + newColor.getGreen() + ", " 48 | + newColor.getBlue()); 49 | } 50 | return this; 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/FilterRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | import com.nccgroup.loggerplusplus.filter.FilterExpression; 4 | import com.nccgroup.loggerplusplus.filter.logfilter.LogTableFilter; 5 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.ColorFilterTable; 6 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.ColorFilterTableModel; 7 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.TagTable; 8 | import com.nccgroup.loggerplusplus.util.userinterface.dialog.TagTableModel; 9 | 10 | import javax.swing.*; 11 | import javax.swing.table.DefaultTableCellRenderer; 12 | import java.awt.*; 13 | 14 | /** 15 | * Created by corey on 22/08/17. 16 | */ 17 | public class FilterRenderer extends DefaultTableCellRenderer { 18 | 19 | @Override 20 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 21 | Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 22 | boolean validFilter; 23 | if (table instanceof ColorFilterTable) { 24 | validFilter = ((ColorFilterTableModel) table.getModel()).validFilterAtRow(row); 25 | } else if (table instanceof TagTable) { 26 | validFilter = ((TagTableModel) table.getModel()).validFilterAtRow(row); 27 | } else { 28 | validFilter = (value instanceof FilterExpression); 29 | } 30 | 31 | if(validFilter){ 32 | c.setBackground(new Color(76,255, 155)); 33 | c.setForeground(Color.BLACK); 34 | }else{ 35 | c.setBackground(new Color(221, 70, 57)); 36 | c.setForeground(Color.WHITE); 37 | } 38 | 39 | return c; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/LeftTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.DefaultTableCellRenderer; 5 | 6 | /** 7 | * Created by corey on 07/09/17. 8 | */ 9 | public class LeftTableCellRenderer extends DefaultTableCellRenderer { 10 | public LeftTableCellRenderer() { 11 | setHorizontalAlignment(SwingConstants.LEFT); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/nccgroup/loggerplusplus/util/userinterface/renderer/TagRenderer.java: -------------------------------------------------------------------------------- 1 | package com.nccgroup.loggerplusplus.util.userinterface.renderer; 2 | 3 | import com.nccgroup.loggerplusplus.filter.tag.Tag; 4 | import org.jdesktop.swingx.HorizontalLayout; 5 | 6 | import javax.swing.*; 7 | import javax.swing.table.TableCellRenderer; 8 | import java.awt.*; 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | import java.util.Collections; 12 | import java.util.Enumeration; 13 | 14 | /** 15 | * Created by corey on 22/08/17. 16 | */ 17 | public class TagRenderer implements TableCellRenderer { 18 | @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 19 | JPanel tagWrapper = new JPanel(); 20 | tagWrapper.setLayout(new HorizontalLayout(2)); 21 | tagWrapper.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); 22 | 23 | if(value instanceof Collection){ 24 | for (Object o : ((Collection) value).toArray()) { 25 | JButton c = new JButton(((Tag) o).getName()); 26 | c.putClientProperty("JButton.buttonType", "roundRect"); 27 | c.setMargin(new Insets(7,4,7,4)); 28 | c.setBackground(((Tag) o).getBackgroundColor()); 29 | c.setForeground(((Tag) o).getForegroundColor()); 30 | tagWrapper.add(c); 31 | } 32 | } 33 | 34 | return tagWrapper; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/AboutMain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/AboutMain.png -------------------------------------------------------------------------------- /src/main/resources/GitHubLogoBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/GitHubLogoBlack.png -------------------------------------------------------------------------------- /src/main/resources/GitHubLogoWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/GitHubLogoWhite.png -------------------------------------------------------------------------------- /src/main/resources/NCCGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/NCCGroup.png -------------------------------------------------------------------------------- /src/main/resources/NCCLarge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/NCCLarge.png -------------------------------------------------------------------------------- /src/main/resources/TwitterLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/logger-plus-plus/6fd5f21b714bb3aef2b34d709b0d05b8ade15d2c/src/main/resources/TwitterLogo.png -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import com.nccgroup.loggerplusplus.LoggerPlusPlus; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public class Test { 9 | public static void main(String[] args) { 10 | try { 11 | Method main = Class.forName("burp.StartBurp").getMethod("main", String[].class); 12 | ArrayList argList = new ArrayList<>(Arrays.stream(args).toList()); 13 | argList.add("--developer-extension-class-name=" + LoggerPlusPlus.class.getName()); 14 | main.invoke(null, (Object) argList.toArray(new String[]{})); 15 | }catch (Exception e){ 16 | System.err.println("Cannot start burp. Check the burp jar is correctly included in the classpath."); 17 | e.printStackTrace(); 18 | } 19 | } 20 | } 21 | --------------------------------------------------------------------------------