Repository: bytebutcher/burp-send-to Branch: master Commit: db1e378bbc5f Files: 80 Total size: 246.0 KB Directory structure: gitextract_4gomkmw3/ ├── .gitignore ├── BappDescription.html ├── BappManifest.bmf ├── README.md └── burp-send-to-extension/ ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src/ └── main/ └── java/ ├── burp/ │ ├── BurpExtender.java │ ├── Cookie.java │ ├── IRequestInfoWrapper.java │ ├── IRequestResponseHolder.java │ ├── IResponseInfoWrapper.java │ ├── RequestInfoWrapper.java │ ├── RequestResponseHolder.java │ └── ResponseInfoWrapper.java ├── com/ │ └── google/ │ └── gson/ │ └── typeadapters/ │ └── RuntimeTypeAdapterFactory.java └── net/ └── bytebutcher/ └── burpsendtoextension/ ├── builder/ │ └── CommandBuilder.java ├── executioner/ │ └── CommandExecutioner.java ├── gui/ │ ├── CommandsChangeListener.java │ ├── SendToAddAdvancedDialog.form │ ├── SendToAddAdvancedDialog.java │ ├── SendToAddAdvancedPlaceholderBehaviourPanel.form │ ├── SendToAddAdvancedPlaceholderBehaviourPanel.java │ ├── SendToAddDialog.form │ ├── SendToAddDialog.java │ ├── SendToContextMenu.java │ ├── SendToContextMenuItem.java │ ├── SendToPreviewDialog.form │ ├── SendToPreviewDialog.java │ ├── SendToRunInTerminalBehaviourChoiceDialog.form │ ├── SendToRunInTerminalBehaviourChoiceDialog.java │ ├── SendToTab.form │ ├── SendToTab.java │ ├── SendToTabSettingsContextMenu.java │ ├── SendToTable.java │ ├── SendToTableListener.java │ ├── action/ │ │ └── SendToContextMenuItemAction.java │ ├── listener/ │ │ └── ToolTipActionListener.java │ └── util/ │ ├── DialogUtil.java │ ├── SelectionUtil.java │ └── WebUtil.java ├── models/ │ ├── CommandObject.java │ ├── Config.java │ ├── Context.java │ ├── ERunInTerminalBehaviour.java │ ├── ERuntimeBehaviour.java │ ├── Placeholders.java │ └── placeholder/ │ ├── AbstractPlaceholder.java │ ├── AbstractRequestInfoPlaceholder.java │ ├── AbstractRequestPlaceholder.java │ ├── AbstractRequestResponseInfoPlaceholder.java │ ├── AbstractRequestResponsePlaceholder.java │ ├── AbstractRequestResponsePlaceholderBase.java │ ├── CookiesPlaceholder.java │ ├── HostPlaceholder.java │ ├── HttpBodyToFilePlaceholder.java │ ├── HttpContentLengthPlaceholder.java │ ├── HttpHeadersToFilePlaceholder.java │ ├── HttpMethodPlaceholder.java │ ├── HttpRequestResponsePlaceholder.java │ ├── HttpStatusCodePlaceholder.java │ ├── IPlaceholder.java │ ├── IPlaceholderParser.java │ ├── IPlaceholderParserFactory.java │ ├── PortPlaceholder.java │ ├── ProtocolPlaceholder.java │ ├── SelectedTextPlaceholder.java │ ├── SelectedTextToFilePlaceholder.java │ ├── UrlPathPlaceholder.java │ ├── UrlPlaceholder.java │ ├── UrlQueryPlaceholder.java │ └── behaviour/ │ ├── CommandSeparatedPlaceholderBehaviour.java │ ├── FileSeparatedPlaceholderBehaviour.java │ ├── IPlaceholderBehaviour.java │ └── StringSeparatedPlaceholderBehaviour.java └── utils/ ├── OsUtils.java └── StringUtils.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ burp-send-to-extension/.idea burp-send-to-extension/.gradle burp-send-to-extension/build burp-send-to-extension/out ================================================ FILE: BappDescription.html ================================================

Adds a customizable "Send to..."-context-menu to your BurpSuite.

Configuration

After loading the extension the "Send to"-Tab contains all necessary options to configure the "Send to"-context-menu.

New context-menu-entries can be added using the "Add"-button. Each entry consists of following fields:

In addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the "Advanced"-button. By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator.

After creating new context-menu-entries using the "Add"-button they can be edited or deleted again using the "Edit"- and "Remove"-button. In addition the order in which they appear in the context-menu can be altered using the "Up"- and "Down"-button.

Terminal Options

The "Terminal Options" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled.

Context-Menu

The "Send to..." context-menu contains all entries which were added in the "Send to"-Tab. In addition you can add new entries via the "Custom command..."-context-menu-entry.

Save and load options

Usually the options of the "Send to"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the "Send to"-Tab and select the appropriate context-menu-entry.

Security Notes

Executing commands based on untrusted input always introduces the risk of command injection. This is especially true when using the %S placeholder. Thus it is recommended to always activate the Show preview option when using the %S placeholder and closely analyse commands in the preview window prior to execution.

================================================ FILE: BappManifest.bmf ================================================ Uuid: f089f1ad056545489139cb9f32900f8e ExtensionType: 1 Name: Custom Send To RepoName: custom-send-to ScreenVersion: 1.4 SerialVersion: 1 MinPlatformVersion: 0 ProOnly: False Author: Thomas Engel ShortDescription: Add a customizable "Send to..." menu to the context menu EntryPoint: burp-send-to-extension/build/libs/burp-send-to-extension-1.4.jar BuildCommand: cd burp-send-to-extension; ./gradlew fatJar ================================================ FILE: README.md ================================================ # Burp-Send-To-Extension Adds a customizable "Send to..."-context-menu to your BurpSuite. ![Burp-Send-To-Extension Tab](images/burp-send-to-extension-intro.png) ## Configuration After loading the extension the "Send to"-Tab contains all necessary options to configure the "Send to"-context-menu. New context-menu-entries can be added using the "Add"-button. Each entry consists of following fields: * **Name:** the name of the context-menu-entry. * **Command:** the command to be executed. You can use following placeholders: * **%H:** will be replaced with the host * **%P:** will be replaced with the port * **%T:** will be replaced with the protocol * **%U:** will be replaced with the url * **%A:** will be replaced with the url path * **%Q:** will be replaced with the url query * **%C:** will be replaced with the cookies * **%L:** will be replaced with the HTTP-content-length * **%M:** will be replaced with the HTTP-method * **%O:** will be replaced with the HTTP-status-code * **%S:** will be replaced with the selected text * **%F:** will be replaced with the path to a temporary file containing the selected text * **%R:** will be replaced with the path to a temporary file containing the content of the focused request/response * **%E:** will be replaced with the path to a temporary file containing the header of the focused request/response * **%B:** will be replaced with the path to a temporary file containing the body of the focused request/response * **Group:** the name of the sub-menu in which this entry will be shown. Can be left blank. * **Run in terminal:** defines whether a terminal-window should appear in which the configured command is executed. By default "xterm" is used as terminal-emulator. You can change the terminal-emulator in the "Miscellaneous Options" to your liking. * **Show preview:** gives you the chance to preview and change the command before executing it. * **Output should replace selection:** will replace the selection with the output of the to be executed command. ![Burp-Send-To-Extension Add-/Edit-Dialog](images/burp-send-to-extension-add-edit-dialog.png) In addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the "Advanced"-button. By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator, or to store all values of a specific placeholder within a file. ![Burp-Send-To-Extension Advanced-Dialog](images/burp-send-to-extension-advanced-dialog.png) After creating new context-menu-entries using the "Add"-button they can be edited or deleted again using the "Edit"- and "Remove"-button. In addition the order in which they appear in the context-menu can be altered using the "Up"- and "Down"-button. ![Burp-Send-To-Extension Tab](images/burp-send-to-extension-tab.png) ## Terminal Options The "Terminal Options" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled. ## Context-Menu The "Send to..." context-menu contains all entries which were added in the "Send to"-Tab. In addition you can add new entries via the "Custom command..."-context-menu-entry. #### Request Field ![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-repeater.png) #### Proxy History ![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-target-sitemap.png) ## Save and load options Usually the options of the "Send to"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the "Send to"-Tab and select the appropriate context-menu-entry. ![Burp-Send-To-Extension Options](images/burp-send-to-extension-options.png) ## Extending the Extension Not satisfied yet? The [Wiki Page](https://github.com/bytebutcher/burp-send-to/wiki/Examples) lists some additional context-menu entries which might come in handy. ## Security Notes Executing commands based on untrusted input always introduces the risk of command injection. This is especially true when using the **%S** placeholder. Thus it is recommended to always activate the **Show preview** option when using the **%S** placeholder and closely analyse commands in the preview window prior to execution. ![Burp-Send-To-Extension Options](images/burp-send-to-extension-forkbomb.png) ## Build This project was built using IntelliJ and Gradle. When you make changes to the source (and especially the GUI) you should apply following settings within Intellij to make sure that everything builds successfully: * File -> Settings -> Editor -> GUI Designer -> Generate GUI into: Java source * File -> Settings -> Build, Execution, Deployment -> Compiler -> Build project automatically * File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using: IntelliJ IDEA When the GUI is not updated correctly you may rebuild the project manually: * Build -> Rebuild Project After that you can execute the "fatJar"-task within the "build.gradle"-file. This will produce a jar in the "build/libs/" directory called "burp-send-to-extension-{version}.jar". ================================================ FILE: burp-send-to-extension/build.gradle ================================================ apply plugin: 'java' group 'net.bytebutcher' version '1.6' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' compile 'com.intellij:forms_rt:7.0.3' compile 'net.portswigger.burp.extender:burp-extender-api:1.7.22' compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6' compile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre' } task fatJar(type: Jar) { baseName = project.name from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } with jar } ================================================ FILE: burp-send-to-extension/gradle/wrapper/gradle-wrapper.properties ================================================ #Sun Mar 17 11:46:47 CET 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip ================================================ FILE: burp-send-to-extension/gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: burp-send-to-extension/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: burp-send-to-extension/settings.gradle ================================================ rootProject.name = 'burp-send-to-extension' ================================================ FILE: burp-send-to-extension/src/main/java/burp/BurpExtender.java ================================================ package burp; import net.bytebutcher.burpsendtoextension.gui.SendToContextMenu; import net.bytebutcher.burpsendtoextension.gui.SendToTab; import net.bytebutcher.burpsendtoextension.gui.SendToTable; import net.bytebutcher.burpsendtoextension.models.Config; import javax.swing.*; import java.awt.*; import java.io.PrintWriter; public class BurpExtender implements IBurpExtender, ITab { private JPanel tab = null; private SendToContextMenu sendToContextMenu; private SendToTable sendToTable; private static IBurpExtenderCallbacks callbacks; private static Config config; private static SendToTab sendToTab = null; private static PrintWriter stdout; private static PrintWriter stderr; @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { BurpExtender.callbacks = callbacks; initLogHandler(callbacks); BurpExtender.printOut("Initializing Send to Extension..."); BurpExtender.callbacks.setExtensionName("Send to"); BurpExtender.printOut("Initializing config..."); BurpExtender.config = new Config(this); BurpExtender.printOut("Initializing tab..."); BurpExtender.sendToTab = new SendToTab(this); BurpExtender.printOut("Registering context menu..."); this.sendToContextMenu = new SendToContextMenu(this, sendToTab.getSendToTableListener()); BurpExtender.callbacks.registerContextMenuFactory(sendToContextMenu); this.tab = sendToTab.getRootPanel(); BurpExtender.printOut("Loading table data..."); this.sendToTable = sendToTab.getSendToTable(); this.sendToTable.addCommandObjects(config.getSendToTableData()); callbacks.addSuiteTab(this); BurpExtender.printOut("----------------------------------------------------------------------"); } private void initLogHandler(IBurpExtenderCallbacks callbacks) { stderr = new PrintWriter(callbacks.getStderr(), true); stdout = new PrintWriter(callbacks.getStdout(), true); } @Override public String getTabCaption() { return "Send to"; } @Override public Component getUiComponent() { return this.tab; } public ImageIcon createImageIcon(String path, String description, int width, int height) { java.net.URL imgURL = getClass().getResource(path); if (imgURL != null) { ImageIcon icon = new ImageIcon(imgURL); Image image = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH); return new ImageIcon(image, description); } else { BurpExtender.printErr("Couldn't find file: " + path); return null; } } public static JFrame getParent() { return sendToTab.getParent(); } public static IBurpExtenderCallbacks getCallbacks() { return callbacks; } public static Config getConfig() { return config; } public static void printOut(String s) { stdout.println(s); } public static void printErr(String s) { stderr.println(s); } } ================================================ FILE: burp-send-to-extension/src/main/java/burp/Cookie.java ================================================ package burp; import com.google.common.collect.Lists; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; /** * Implements the ICookie interface which holds details about an HTTP cookie. * @author Thomas Engel * @author August Detlefsen [augustd at codemagi dot com] */ public class Cookie implements ICookie { private String name; private String value; private String domain; private String path; private Date expiration; private Long maxAge; private Boolean secure = false; private Boolean httpOnly = false; public Cookie(String name, String value) { this.name = name; this.value = value; } public Cookie(ICookie cookie) { this.name = cookie.getName(); this.value = cookie.getValue(); this.domain = cookie.getDomain(); this.path = cookie.getPath(); this.expiration = cookie.getExpiration(); } /** * Parses a list of HTTP response header fields containing the raw cookie * _value_ (Minus "Set-Cookie:"). * * @param rawCookies A list of string containing the raw cookie * @return A list of ICookie objects parsed from a list of raw cookie strings */ public static List parseResponseCookies(List rawCookies) { return rawCookies.stream().map(Cookie::parseResponseCookie).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); } /** * Parses a cookie from a String containing the raw HTTP response header * _value_ (Minus "Set-Cookie:"). * * @param rawCookie A String containing the raw cookie * @return A Cookie object parsed from the raw cookie string */ private static Optional parseResponseCookie(String rawCookie) { String[] rawCookieParams = rawCookie.split(";"); //get the cookie name, check for valid cookie String[] rawCookieNameAndValue = rawCookieParams[0].split("="); String cookieName = rawCookieNameAndValue[0].trim(); if (cookieName.isEmpty()) { BurpExtender.printErr("Invalid cookie: missing name"); return Optional.empty(); } //get the cookie value String cookieValue = rawCookieNameAndValue[1].trim(); //construct output Cookie output = new Cookie(cookieName, cookieValue); //parse other cookie params for (int i = 1; i < rawCookieParams.length; i++) { String[] rawCookieParam = rawCookieParams[i].trim().split("="); String paramName = rawCookieParam[0].trim(); if ("secure".equalsIgnoreCase(paramName)) { output.setSecure(true); } else if ("HttpOnly".equalsIgnoreCase(paramName)) { output.setHttpOnly(true); } else { if (rawCookieParam.length != 2) { //attribute not a flag or missing value continue; } String paramValue = rawCookieParam[1].trim(); if ("expires".equalsIgnoreCase(paramName)) { try { SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz"); Date expiryDate = format.parse(paramValue); output.setExpiration(expiryDate); } catch (Exception e) { //couldn't parse date, ignore BurpExtender.printErr("WARNING: unable to parse cookie expiration: " + paramValue); } } else if ("max-age".equalsIgnoreCase(paramName)) { long maxAge = Long.parseLong(paramValue); output.setMaxAge(maxAge); } else if ("domain".equalsIgnoreCase(paramName)) { output.setDomain(paramValue); } else if ("path".equalsIgnoreCase(paramName)) { output.setPath(paramValue); } } } return Optional.of(output); } /** * Parses cookies from a list of raw HTTP request headers * _value_ (Minus "Cookie:"). * * @param rawCookies A list of strings containing the raw cookie * @return A list of ICookie objects parsed from a list of raw cookie strings */ public static List parseRequestCookies(List rawCookies) { return rawCookies.stream().map(Cookie::parseRequestCookies).flatMap(Collection::stream).collect(Collectors.toList()); } /** * Parses a cookie from a String containing the raw HTTP request header * _value_ (Minus "Cookie:"). * * @param rawCookie A String containing the raw cookie * @return A list of Cookie objects parsed from the raw cookie string */ public static List parseRequestCookies(String rawCookie) { List cookies = Lists.newArrayList(); String[] rawCookieParams = rawCookie.split(";"); for (String rawCookieParam : rawCookieParams) { //get the cookie name, check for valid cookie String[] rawCookieNameAndValue = rawCookieParam.split("="); String cookieName = rawCookieNameAndValue[0].trim(); if (cookieName.isEmpty() || !rawCookieParam.contains("=")) { BurpExtender.printErr("Invalid cookie: missing name"); continue; } //get the cookie value String cookieValue = ""; if (rawCookieNameAndValue.length != 1) { cookieValue = rawCookieNameAndValue[1].trim(); } //construct output cookies.add(new Cookie(cookieName, cookieValue)); } return cookies; } @Override public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } @Override public String getPath() { return path; } public void setPath(String path) { this.path = path; } @Override public Date getExpiration() { return expiration; } public void setExpiration(Date expiration) { this.expiration = expiration; } public Long getMaxAge() { return maxAge; } public void setMaxAge(Long maxAge) { this.maxAge = maxAge; } public Boolean getSecure() { return secure; } public void setSecure(Boolean secure) { this.secure = secure; } public Boolean getHttpOnly() { return httpOnly; } public void setHttpOnly(Boolean httpOnly) { this.httpOnly = httpOnly; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Cookie cookie = (Cookie) o; return Objects.equals(name, cookie.name) && Objects.equals(value, cookie.value) && Objects.equals(domain, cookie.domain) && Objects.equals(path, cookie.path) && Objects.equals(expiration, cookie.expiration) && Objects.equals(maxAge, cookie.maxAge) && Objects.equals(secure, cookie.secure) && Objects.equals(httpOnly, cookie.httpOnly); } @Override public int hashCode() { return Objects.hash(name, value, domain, path, expiration, maxAge, secure, httpOnly); } } ================================================ FILE: burp-send-to-extension/src/main/java/burp/IRequestInfoWrapper.java ================================================ package burp; import java.util.List; public interface IRequestInfoWrapper extends IRequestInfo { List getCookies(); String getBody(); } ================================================ FILE: burp-send-to-extension/src/main/java/burp/IRequestResponseHolder.java ================================================ package burp; public interface IRequestResponseHolder { IRequestInfo getRequestInfo(); IResponseInfo getResponseInfo(); IBurpExtenderCallbacks getBurpExtenderCallbacks(); IHttpRequestResponse getHttpRequestResponse(); } ================================================ FILE: burp-send-to-extension/src/main/java/burp/IResponseInfoWrapper.java ================================================ package burp; import java.util.List; public interface IResponseInfoWrapper extends IResponseInfo { List getCookies(); String getBody(); } ================================================ FILE: burp-send-to-extension/src/main/java/burp/RequestInfoWrapper.java ================================================ package burp; import java.net.URL; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class RequestInfoWrapper implements IRequestInfoWrapper { private IHttpRequestResponse httpRequestResponse; private IRequestInfo requestInfo; private List cookies; private String requestBody; public RequestInfoWrapper(IHttpRequestResponse httpRequestResponse, IRequestInfo requestInfo) { this.httpRequestResponse = httpRequestResponse; this.requestInfo = requestInfo; } @Override public List getCookies() { if (cookies == null) { String cookieHeaderPrefix = "cookie: "; cookies = Cookie.parseRequestCookies(requestInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList())); } return cookies; } @Override public String getBody() { if (requestBody == null) { byte[] request = httpRequestResponse.getRequest(); int bodyOffset = this.getBodyOffset(); requestBody = new String(Arrays.copyOfRange(request, bodyOffset, request.length)); } return requestBody; } @Override public String getMethod() { return requestInfo.getMethod(); } @Override public URL getUrl() { return requestInfo.getUrl(); } @Override public List getHeaders() { return requestInfo.getHeaders(); } @Override public List getParameters() { return requestInfo.getParameters(); } @Override public int getBodyOffset() { return requestInfo.getBodyOffset(); } @Override public byte getContentType() { return requestInfo.getContentType(); } } ================================================ FILE: burp-send-to-extension/src/main/java/burp/RequestResponseHolder.java ================================================ package burp; public class RequestResponseHolder implements IRequestResponseHolder { private final IBurpExtenderCallbacks burpExtenderCallbacks; private final IHttpRequestResponse httpRequestResponse; private IRequestInfoWrapper requestInfo; private IResponseInfoWrapper responseInfo; public RequestResponseHolder(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse httpRequestResponse) { this.burpExtenderCallbacks = burpExtenderCallbacks; this.httpRequestResponse = httpRequestResponse; } @Override public IRequestInfoWrapper getRequestInfo() { if (requestInfo == null) { requestInfo = new RequestInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeRequest(httpRequestResponse.getHttpService(), httpRequestResponse.getRequest())); } return requestInfo; } @Override public IResponseInfoWrapper getResponseInfo() { if (responseInfo == null) { responseInfo = new ResponseInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeResponse(httpRequestResponse.getResponse())); } return responseInfo; } @Override public IBurpExtenderCallbacks getBurpExtenderCallbacks() { return burpExtenderCallbacks; } @Override public IHttpRequestResponse getHttpRequestResponse() { return httpRequestResponse; } } ================================================ FILE: burp-send-to-extension/src/main/java/burp/ResponseInfoWrapper.java ================================================ package burp; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class ResponseInfoWrapper implements IResponseInfoWrapper { private IHttpRequestResponse httpRequestResponse; private IResponseInfo responseInfo; private List cookies; private String responseBody; public ResponseInfoWrapper(IHttpRequestResponse httpRequestResponse, IResponseInfo responseInfo) { this.httpRequestResponse = httpRequestResponse; this.responseInfo = responseInfo; } @Override public List getHeaders() { return responseInfo.getHeaders(); } @Override public int getBodyOffset() { return responseInfo.getBodyOffset(); } @Override public short getStatusCode() { return responseInfo.getStatusCode(); } @Override public List getCookies() { if (cookies == null) { String cookieHeaderPrefix = "set-cookie: "; cookies = Cookie.parseResponseCookies(responseInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList())); } return cookies; } @Override public String getStatedMimeType() { return responseInfo.getStatedMimeType(); } @Override public String getInferredMimeType() { return responseInfo.getInferredMimeType(); } @Override public String getBody() { if (responseBody == null) { byte[] response = httpRequestResponse.getResponse(); int bodyOffset = this.getBodyOffset(); responseBody = new String(Arrays.copyOfRange(response, bodyOffset, response.length)); } return responseBody; } } ================================================ FILE: burp-send-to-extension/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java ================================================ /* * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gson.typeadapters; import com.google.gson.*; import com.google.gson.internal.Streams; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; /** * Adapts values whose runtime type may differ from their declaration type. This * is necessary when a field's type is not the same type that GSON should create * when deserializing that field. For example, consider these types: *
   {@code
 *   abstract class Shape {
 *     int x;
 *     int y;
 *   }
 *   class Circle extends Shape {
 *     int radius;
 *   }
 *   class Rectangle extends Shape {
 *     int width;
 *     int height;
 *   }
 *   class Diamond extends Shape {
 *     int width;
 *     int height;
 *   }
 *   class Drawing {
 *     Shape bottomShape;
 *     Shape topShape;
 *   }
 * }
*

Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond?

   {@code
 *   {
 *     "bottomShape": {
 *       "width": 10,
 *       "height": 5,
 *       "x": 0,
 *       "y": 0
 *     },
 *     "topShape": {
 *       "radius": 2,
 *       "x": 4,
 *       "y": 1
 *     }
 *   }}
* This class addresses this problem by adding type information to the * serialized JSON and honoring that type information when the JSON is * deserialized:
   {@code
 *   {
 *     "bottomShape": {
 *       "type": "Diamond",
 *       "width": 10,
 *       "height": 5,
 *       "x": 0,
 *       "y": 0
 *     },
 *     "topShape": {
 *       "type": "Circle",
 *       "radius": 2,
 *       "x": 4,
 *       "y": 1
 *     }
 *   }}
* Both the type field name ({@code "type"}) and the type labels ({@code * "Rectangle"}) are configurable. * *

Registering Types

* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field * name to the {@link #of} factory method. If you don't supply an explicit type * field name, {@code "type"} will be used.
   {@code
 *   RuntimeTypeAdapterFactory shapeAdapterFactory
 *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
 * }
* Next register all of your subtypes. Every subtype must be explicitly * registered. This protects your application from injection attacks. If you * don't supply an explicit type label, the type's simple name will be used. *
   {@code
 *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
 *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
 *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
 * }
* Finally, register the type adapter factory in your application's GSON builder: *
   {@code
 *   Gson gson = new GsonBuilder()
 *       .registerTypeAdapterFactory(shapeAdapterFactory)
 *       .create();
 * }
* Like {@code GsonBuilder}, this API supports chaining:
   {@code
 *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
 *       .registerSubtype(Rectangle.class)
 *       .registerSubtype(Circle.class)
 *       .registerSubtype(Diamond.class);
 * }
* *

Serialization and deserialization

* In order to serialize and deserialize a polymorphic object, * you must specify the base type explicitly. *
   {@code
 *   Diamond diamond = new Diamond();
 *   String json = gson.toJson(diamond, Shape.class);
 * }
* And then: *
   {@code
 *   Shape shape = gson.fromJson(json, Shape.class);
 * }
*/ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; private final Map> labelToSubtype = new LinkedHashMap>(); private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private final boolean maintainType; private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName, boolean maintainType) { if (typeFieldName == null || baseType == null) { throw new NullPointerException(); } this.baseType = baseType; this.typeFieldName = typeFieldName; this.maintainType = maintainType; } /** * Creates a new runtime type adapter using for {@code baseType} using {@code * typeFieldName} as the type field name. Type field names are case sensitive. * {@code maintainType} flag decide if the type will be stored in pojo or not. */ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { return new RuntimeTypeAdapterFactory(baseType, typeFieldName, maintainType); } /** * Creates a new runtime type adapter using for {@code baseType} using {@code * typeFieldName} as the type field name. Type field names are case sensitive. */ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { return new RuntimeTypeAdapterFactory(baseType, typeFieldName, false); } /** * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as * the type field name. */ public static RuntimeTypeAdapterFactory of(Class baseType) { return new RuntimeTypeAdapterFactory(baseType, "type", false); } /** * Registers {@code type} identified by {@code label}. Labels are case * sensitive. * * @throws IllegalArgumentException if either {@code type} or {@code label} * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { if (type == null || label == null) { throw new NullPointerException(); } if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { throw new IllegalArgumentException("types and labels must be unique"); } labelToSubtype.put(label, type); subtypeToLabel.put(type, label); return this; } /** * Registers {@code type} identified by its {@link Class#getSimpleName simple * name}. Labels are case sensitive. * * @throws IllegalArgumentException if either {@code type} or its simple name * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory registerSubtype(Class type) { return registerSubtype(type, type.getSimpleName()); } public TypeAdapter create(Gson gson, TypeToken type) { if (type.getRawType() != baseType) { return null; } final Map> labelToDelegate = new LinkedHashMap>(); final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); for (Map.Entry> entry : labelToSubtype.entrySet()) { TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); labelToDelegate.put(entry.getKey(), delegate); subtypeToDelegate.put(entry.getValue(), delegate); } return new TypeAdapter() { @Override public R read(JsonReader in) throws IOException { JsonElement jsonElement = Streams.parse(in); JsonElement labelJsonElement; if (maintainType) { labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); } else { labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); } if (labelJsonElement == null) { throw new JsonParseException("cannot deserialize " + baseType + " because it does not define a field named " + typeFieldName); } String label = labelJsonElement.getAsString(); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); if (delegate == null) { throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label + "; did you forget to register a subtype?"); } return delegate.fromJsonTree(jsonElement); } @Override public void write(JsonWriter out, R value) throws IOException { Class srcType = value.getClass(); String label = subtypeToLabel.get(srcType); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); if (delegate == null) { throw new JsonParseException("cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); } JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); if (maintainType) { Streams.write(jsonObject, out); return; } JsonObject clone = new JsonObject(); if (jsonObject.has(typeFieldName)) { throw new JsonParseException("cannot serialize " + srcType.getName() + " because it already defines a field named " + typeFieldName); } clone.add(typeFieldName, new JsonPrimitive(label)); for (Map.Entry e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } Streams.write(clone, out); } }.nullSafe(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/builder/CommandBuilder.java ================================================ package net.bytebutcher.burpsendtoextension.builder; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.Context; import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; import java.io.File; import java.io.PrintWriter; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; public class CommandBuilder { // The model of the context menu entry with the format string and various options. private final CommandObject commandObject; // List of selected messages containing the placeholders and their values. private final List> placeholderMap; // List of placeholders and merged values. private final Map placeholderValues; private final Context context; private static class Placeholder { private final String name; private final IPlaceholderBehaviour behaviour; public Placeholder(CommandObject.Placeholder placeholder) { this.name = placeholder.getName(); this.behaviour = placeholder.getBehaviour(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Placeholder that = (Placeholder) o; return Objects.equals(name, that.name) && Objects.equals(behaviour, that.behaviour); } @Override public int hashCode() { return Objects.hash(name, behaviour); } } public CommandBuilder(CommandObject commandObject, List> placeholderMap, Context context) throws Exception { this.commandObject = commandObject; this.placeholderMap = placeholderMap; this.context = context; this.placeholderValues = initPlaceholderValues(); } public Map initPlaceholderValues() throws Exception { Map placeholderValues = Maps.newHashMap(); for (CommandObject.Placeholder coPlaceholder : commandObject.getPlaceholders()) { Placeholder cbPlaceholder = new Placeholder(coPlaceholder); if (!placeholderValues.containsKey(cbPlaceholder)) { if (coPlaceholder.getBehaviour() instanceof StringSeparatedPlaceholderBehaviour){ // combine the values of all messages using the defined placeholder separator placeholderValues.put(cbPlaceholder, commandObject.getValid(placeholderMap, context).stream() .map(m -> m.get(coPlaceholder.getName())) .map(iPlaceholder -> iPlaceholder.getValue(context)) .collect(Collectors.joining(((StringSeparatedPlaceholderBehaviour) coPlaceholder.getBehaviour()).getSeparator()))); } else if (coPlaceholder.getBehaviour() instanceof FileSeparatedPlaceholderBehaviour){ // combine the values of all messages and write them into a file. placeholderValues.put(cbPlaceholder, writeToFile(commandObject.getValid(placeholderMap, context).stream() .map(m -> m.get(coPlaceholder.getName())) .map(iPlaceholder -> iPlaceholder.getValue(context)) .collect(Collectors.joining("\n")))); } } } return placeholderValues; } /** * Returns the command while all placeholders are replaced with their associated value as String. * @throws Exception when retrieving/replacing a placeholder failed. */ public String build() throws Exception { try { List result = Lists.newArrayList(); boolean containsCommandSeparatedPlaceholderBehaviour = commandObject.getPlaceholders().stream() .map(CommandObject.Placeholder::getBehaviour) .anyMatch(c -> c instanceof CommandSeparatedPlaceholderBehaviour); if (containsCommandSeparatedPlaceholderBehaviour) { for (int messageIndex = 0; messageIndex < placeholderMap.size(); messageIndex++) { result.add(buildByMessage(messageIndex)); } } else { result.add(buildByMessage(0)); } return String.join("\n", result); } catch (RuntimeException e) { // Rethrow from unchecked to checked exception. We only deal with RuntimeException here, since streams // (here: placeholderMap.stream()) does not handle checked exceptions well. throw new Exception(e); } } private String buildByMessage(int messageIndex) throws Exception { StringBuffer format = new StringBuffer(commandObject.getFormat()); // For each placeholder, starting from the placeholder at the very end, replace it with the value from the message. List placeholders = commandObject.getPlaceholders().stream().sorted( Comparator.comparing(CommandObject.Placeholder::getEnd).reversed() ).collect(Collectors.toList()); for (CommandObject.Placeholder placeholder : placeholders) { replaceCommandPlaceholder(placeholder, placeholderMap, messageIndex, format, context); } return format.toString(); } private void replaceCommandPlaceholder(CommandObject.Placeholder placeholder, List> placeholderMap, int messageIndex, StringBuffer command, Context context) throws Exception { String value; if (placeholderValues.containsKey(new Placeholder(placeholder))) { // merged values value = placeholderValues.get(new Placeholder(placeholder)); } else { // command separated - use the value from the actual message value = placeholderMap.get(messageIndex).get(placeholder.getName()).getValue(context); } command.replace(placeholder.getStart(), placeholder.getEnd(), value); } private String writeToFile(String value) throws Exception { try { File tmp = File.createTempFile("burp_", ".snd"); PrintWriter out = new PrintWriter(tmp.getPath()); out.write(value); out.flush(); return tmp.getAbsolutePath(); } catch (RuntimeException e) { throw new Exception(this.getClass().getSimpleName() + ": Error writing to temporary file!", e); } } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/executioner/CommandExecutioner.java ================================================ package net.bytebutcher.burpsendtoextension.executioner; import burp.BurpExtender; import burp.IHttpRequestResponse; import com.google.common.collect.Lists; import net.bytebutcher.burpsendtoextension.gui.util.SelectionUtil; import net.bytebutcher.burpsendtoextension.models.Context; import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; import net.bytebutcher.burpsendtoextension.utils.OsUtils; import net.bytebutcher.burpsendtoextension.utils.StringUtils; import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class CommandExecutioner { private final ERunInTerminalBehaviour runInTerminalBehaviour; private final boolean shouldOutputReplaceSelection; private final Context context; public CommandExecutioner(boolean shouldOutputReplaceSelection, Context context) { this.runInTerminalBehaviour = null; this.shouldOutputReplaceSelection = shouldOutputReplaceSelection; this.context = context; } public CommandExecutioner(ERunInTerminalBehaviour runInTerminalBehaviour, boolean shouldOutputReplaceSelection, Context context) { this.runInTerminalBehaviour = runInTerminalBehaviour; this.shouldOutputReplaceSelection = shouldOutputReplaceSelection; this.context = context; } public void execute(String commands) throws Exception { if (commands != null) { List commandOutput = Lists.newArrayList(); if (runInTerminalBehaviour != null && runInTerminalBehaviour == ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL) { // Run commands sequential within terminal String command = Arrays.stream(commands.split("\n")).collect(Collectors.joining(" ; ")); execute(command, commandOutput); } else { // Run commands in parallel (within separate terminals or in background) for (String command : commands.split("\n")) { execute(command, commandOutput); } } if (!commandOutput.isEmpty()) { replaceSelectedText(context, commandOutput.stream().collect(Collectors.joining("\n"))); } } } private void execute(String command, List commandOutput) throws IOException { ProcessBuilder commandProcessBuilder = getProcessBuilder(command); logCommandToBeExecuted(commandProcessBuilder.command().toArray(new String[commandProcessBuilder.command().size()])); Process process = commandProcessBuilder.start(); if (shouldOutputReplaceSelection) { commandOutput.add(StringUtils.fromInputStream(process.getInputStream())); } } private ProcessBuilder getProcessBuilder(String command) { if (runInTerminalBehaviour != null) { return new ProcessBuilder(formatCommandForRunningInTerminal(command)); } else { return new ProcessBuilder(formatCommandForRunningOnOperatingSystem(command)); } } private String[] formatCommandForRunningOnOperatingSystem(String command) { String[] commandToBeExecuted; if (OsUtils.isWindows()) { commandToBeExecuted = new String[]{"cmd", "/c", command}; } else { commandToBeExecuted = new String[]{"/bin/bash", "-c", command}; } return commandToBeExecuted; } private String[] formatCommandForRunningInTerminal(String command) { String[] commandToBeExecuted = BurpExtender.getConfig().getRunInTerminalCommand().split(" "); for (int i = 0; i < commandToBeExecuted.length; i++) { String commandPart = commandToBeExecuted[i]; if ("%C".equals(commandPart)) { commandToBeExecuted[i] = command; } } return commandToBeExecuted; } private void replaceSelectedText(Context context, String replaceText) throws Exception { if (context.getSelectedMessages() != null && context.getSelectedMessages().length > 0) { IHttpRequestResponse message = context.getSelectedMessages()[0]; switch (context.getOrigin()) { case HTTP_REQUEST: message.setRequest(SelectionUtil.replaceSelectedText(message.getRequest(), context.getSelectionBounds(), replaceText)); break; case HTTP_RESPONSE: message.setResponse(SelectionUtil.replaceSelectedText(message.getResponse(), context.getSelectionBounds(), replaceText)); break; } } } private void logCommandToBeExecuted(String[] commandToBeExecuted) { String commandToBeExecutedWithoutControlCharacters = String.join(" ", commandToBeExecuted).replaceAll("[\u0000-\u001f]", ""); String dateTime = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss")); BurpExtender.printOut("[" + dateTime + "] " + commandToBeExecutedWithoutControlCharacters); BurpExtender.printOut("----------------------------------------------------------------------"); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/CommandsChangeListener.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import net.bytebutcher.burpsendtoextension.models.CommandObject; import java.util.List; public interface CommandsChangeListener { void commandsChanged(List commandObjects); } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import com.google.common.collect.Lists; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.models.CommandObject; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.List; public class SendToAddAdvancedDialog extends JDialog { private final Component parent; private final List placeholders; private JPanel contentPane; private JButton buttonOK; private JButton buttonCancel; private JPanel pnlPlaceholderBehaviour; private boolean success = false; public SendToAddAdvancedDialog(Component parent, List placeholders) { this.parent = parent; this.placeholders = placeholders; $$$setupUI$$$(); setContentPane(contentPane); setTitle("Customize Placeholder Behaviour"); setModal(true); getRootPane().setDefaultButton(buttonOK); buttonOK.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { onOK(); } }); buttonCancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { onCancel(); } }); // call onCancel() when cross is clicked setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { onCancel(); } }); // call onCancel() on ESCAPE contentPane.registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent e) { onCancel(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); } public List getPlaceholders() { List placeholders = Lists.newArrayList(); for (Component component : pnlPlaceholderBehaviour.getComponents()) { placeholders.add(((SendToAddAdvancedPlaceholderBehaviourPanel) component).getPlaceholder()); } return placeholders; } private void onOK() { success = true; dispose(); } private void onCancel() { success = false; dispose(); } public boolean run() { for (CommandObject.Placeholder placeholder : placeholders) { pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel(placeholder)); } this.setSize(450, 250); int x = DialogUtil.getX(parent, this); int y = DialogUtil.getY(parent, this); this.setLocation(x, y); this.pack(); this.setVisible(true); return success; } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { createUIComponents(); contentPane = new JPanel(); contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); final Spacer spacer1 = new Spacer(); panel1.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); final JPanel panel2 = new JPanel(); panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); panel1.add(panel2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); buttonOK = new JButton(); buttonOK.setText("OK"); panel2.add(buttonOK, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); buttonCancel = new JButton(); buttonCancel.setText("Cancel"); panel2.add(buttonCancel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel3 = new JPanel(); panel3.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); contentPane.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label1 = new JLabel(); label1.setText("Customize the behaviour of each placeholder when multiple HTTP messages are selected:"); panel3.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel4 = new JPanel(); panel4.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); panel3.add(panel4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final Spacer spacer2 = new Spacer(); panel4.add(spacer2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); panel4.add(pnlPlaceholderBehaviour, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return contentPane; } private void createUIComponents() { pnlPlaceholderBehaviour = new JPanel(); pnlPlaceholderBehaviour.setLayout(new BoxLayout(this.pnlPlaceholderBehaviour, BoxLayout.Y_AXIS)); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SendToAddAdvancedPlaceholderBehaviourPanel extends JPanel { private final CommandObject.Placeholder placeholder; private JTextField txtSeparator; private JPanel pnlMain; private JComboBox cmbSeperator; private JLabel lblPlaceholder; public SendToAddAdvancedPlaceholderBehaviourPanel(CommandObject.Placeholder placeholder) { this.placeholder = placeholder; this.add(pnlMain); this.lblPlaceholder.setText(placeholder.getName()); initFields(); initEventListener(); } private void initFields() { IPlaceholderBehaviour placeholderBehaviour = placeholder.getBehaviour(); if (placeholderBehaviour instanceof StringSeparatedPlaceholderBehaviour) { this.txtSeparator.setText(((StringSeparatedPlaceholderBehaviour) placeholderBehaviour).getSeparator()); this.txtSeparator.setEnabled(true); this.cmbSeperator.setSelectedIndex(1); } else if (placeholderBehaviour instanceof FileSeparatedPlaceholderBehaviour) { this.txtSeparator.setText(""); this.txtSeparator.setEnabled(false); this.cmbSeperator.setSelectedIndex(2); } else { this.txtSeparator.setText(""); this.txtSeparator.setEnabled(false); this.cmbSeperator.setSelectedIndex(0); } } private void initEventListener() { this.cmbSeperator.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (cmbSeperator.getSelectedIndex() == 1) { txtSeparator.setEnabled(true); txtSeparator.setText(""); } else { txtSeparator.setEnabled(false); txtSeparator.setText(""); } } }); } public CommandObject.Placeholder getPlaceholder() { switch (cmbSeperator.getSelectedIndex()) { case 1: placeholder.setBehaviour(new StringSeparatedPlaceholderBehaviour(txtSeparator.getText())); break; case 2: placeholder.setBehaviour(new FileSeparatedPlaceholderBehaviour()); break; default: placeholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour()); break; } return placeholder; } { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< // DO NOT EDIT OR ADD ANY CODE HERE! $$$setupUI$$$(); } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { pnlMain = new JPanel(); pnlMain.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); txtSeparator = new JTextField(); pnlMain.add(txtSeparator, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); cmbSeperator = new JComboBox(); final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel(); defaultComboBoxModel1.addElement("split into separate commands"); defaultComboBoxModel1.addElement("separate by string"); defaultComboBoxModel1.addElement("merge into file"); cmbSeperator.setModel(defaultComboBoxModel1); pnlMain.add(cmbSeperator, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); lblPlaceholder = new JLabel(); lblPlaceholder.setText("..."); pnlMain.add(lblPlaceholder, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return pnlMain; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddDialog.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddDialog.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import com.google.common.collect.Lists; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import net.bytebutcher.burpsendtoextension.gui.listener.ToolTipActionListener; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.ERuntimeBehaviour; import net.bytebutcher.burpsendtoextension.models.Placeholders; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ActionEvent; import java.util.List; public class SendToAddDialog { private JTextField txtName; private JTextField txtCommand; private JButton btnCancel; private JButton btnOk; private JPanel formPanel; private JButton btnCommandHelp; private JCheckBox chkShowPreviewPriorToExecution; private JTextField txtGroup; private JRadioButton chkRunInTerminal; private JRadioButton chkOutputShouldReplaceSelection; private JRadioButton chkRunInBackground; private JButton btnAdvanced; private final JDialog dialog; private boolean success = false; private AbstractAction onOkActionListener; private AbstractAction onCancelActionListener; // This list is used to check whether the currently entered command object already exists (duplicate-check). private List commandObjects; private List placeholders = Lists.newArrayList(); public SendToAddDialog(JFrame parent, String title, List commandObjects) { this.commandObjects = commandObjects; this.dialog = initDialog(parent, title); initEventListener(); initKeyboardShortcuts(); initButtonState(); } public SendToAddDialog(JFrame parent, String title, List commandObjects, CommandObject commandObject) { this(parent, title, commandObjects); commandObjects.remove(commandObject); txtName.setText(commandObject.getName()); txtCommand.setText(commandObject.getFormat()); txtGroup.setText(commandObject.getGroup()); chkShowPreviewPriorToExecution.setSelected(commandObject.shouldShowPreview()); chkRunInBackground.setSelected(commandObject.shouldRunInBackground()); chkRunInTerminal.setSelected(commandObject.shouldRunInTerminal()); chkOutputShouldReplaceSelection.setSelected(commandObject.shouldOutputReplaceSelection()); placeholders = Lists.newArrayList(commandObject.getPlaceholders()); } private void initKeyboardShortcuts() { bindKeyStrokeToAction("ESCAPE", onCancelActionListener); bindKeyStrokeToAction("ENTER", onOkActionListener); } private void bindKeyStrokeToAction(String keyStroke, Action action) { KeyStroke stroke = KeyStroke.getKeyStroke(keyStroke); InputMap inputMap = formPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(stroke, keyStroke); formPanel.getActionMap().put(keyStroke, action); } private void initEventListener() { txtCommand.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { updateAdvancedButton(); } @Override public void removeUpdate(DocumentEvent e) { updateAdvancedButton(); } @Override public void changedUpdate(DocumentEvent e) { updateAdvancedButton(); } private void updateAdvancedButton() { btnAdvanced.setEnabled(!Placeholders.get(txtCommand.getText()).isEmpty()); } }); btnAdvanced.addActionListener((e) -> { placeholders = getCommandObject().getPlaceholders(); SendToAddAdvancedDialog dialog = new SendToAddAdvancedDialog(this.dialog, placeholders); if (dialog.run()) { placeholders = dialog.getPlaceholders(); } }); onOkActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (getName().isEmpty()) { DialogUtil.showErrorDialog( dialog, "Name should not be empty!", "Name is empty!" ); return; } if (!commandObjects.stream().noneMatch(commandObject -> getName().equals(commandObject.getName()) && getGroup().equals(commandObject.getGroup()))) { DialogUtil.showErrorDialog( dialog, "Name already exists within the specified group!", "Combination of name and group already exists!" ); return; } if (getCommand().isEmpty()) { DialogUtil.showErrorDialog( dialog, "Command should not be empty!", "Command is empty!" ); return; } success = true; dialog.dispose(); } }; btnOk.addActionListener(onOkActionListener); onCancelActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { success = false; dialog.dispose(); } }; btnCancel.addActionListener(onCancelActionListener); btnCommandHelp.addActionListener(new ToolTipActionListener(btnCommandHelp, "" + "" + "

%H = Host

" + "

%P = Port

" + "

%T = Protocol

" + "

%U = URL

" + "

%A = URL-Path

" + "

%Q = URL-Query

" + "

%C = Cookies

" + "

%L = HTTP-Content-Length

" + "

%M = HTTP-Method

" + "

%O = HTTP-Status-Code

" + "

%S = Selected text

" + "

%F = Path to file containing selected text

" + "

%R = Path to file containing HTTP-request/-response

" + "

%B = Path to file containing body of HTTP-request/-response

" + "

%E = Path to file containing header of HTTP-request/-response

" + "") ); } private void initButtonState() { btnAdvanced.setEnabled(!Placeholders.get(txtCommand.getText()).isEmpty()); } private JDialog initDialog(JFrame parent, String title) { JDialog dialog = new JDialog(parent, title, true); dialog.getContentPane().add(this.getRootPanel()); dialog.setSize(450, 250); int x = DialogUtil.getX(parent, dialog); int y = DialogUtil.getY(parent, dialog); dialog.setLocation(x, y); dialog.pack(); return dialog; } public boolean run() { this.dialog.setVisible(true); return this.success; } private JPanel getRootPanel() { return formPanel; } private String getName() { return txtName.getText(); } private String getCommand() { return txtCommand.getText(); } private String getGroup() { return txtGroup.getText(); } private ERuntimeBehaviour getRuntimeBehaviour() { if (chkRunInTerminal.isSelected()) { return ERuntimeBehaviour.RUN_IN_TERMINAL; } if (chkOutputShouldReplaceSelection.isSelected()) { return ERuntimeBehaviour.OUTPUT_SHOULD_REPLACE_SELECTION; } if (chkRunInBackground.isSelected()) { return ERuntimeBehaviour.RUN_IN_BACKGROUND; } BurpExtender.printErr("Error parsing runtime behaviour. Please file a bug report if you encounter this error more often."); return ERuntimeBehaviour.RUN_IN_TERMINAL; // Return sane default } private boolean shouldShowPreview() { return chkShowPreviewPriorToExecution.isSelected(); } private List getPlaceholders() { return placeholders; } public CommandObject getCommandObject() { return new CommandObject(getName(), getCommand(), getGroup(), getRuntimeBehaviour(), shouldShowPreview(), getPlaceholders()); } { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< // DO NOT EDIT OR ADD ANY CODE HERE! $$$setupUI$$$(); } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { formPanel = new JPanel(); formPanel.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1)); final JLabel label1 = new JLabel(); label1.setText("Enter the details for the \"Send to...\" context menu entry."); formPanel.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridBagLayout()); formPanel.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label2 = new JLabel(); label2.setText("Name:"); label2.setDisplayedMnemonic('N'); label2.setDisplayedMnemonicIndex(0); GridBagConstraints gbc; gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(2, 2, 2, 10); panel1.add(label2, gbc); final JLabel label3 = new JLabel(); label3.setText("Command:"); label3.setDisplayedMnemonic('M'); label3.setDisplayedMnemonicIndex(2); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(2, 2, 2, 10); panel1.add(label3, gbc); final JPanel panel2 = new JPanel(); panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; gbc.fill = GridBagConstraints.BOTH; panel1.add(panel2, gbc); txtCommand = new JTextField(); txtCommand.setText(""); panel2.add(txtCommand, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); btnCommandHelp = new JButton(); btnCommandHelp.setText("?"); panel2.add(btnCommandHelp, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label4 = new JLabel(); label4.setText("Group:"); label4.setDisplayedMnemonic('G'); label4.setDisplayedMnemonicIndex(0); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(2, 2, 2, 10); panel1.add(label4, gbc); chkRunInTerminal = new JRadioButton(); chkRunInTerminal.setText("Run in terminal"); chkRunInTerminal.setMnemonic('T'); chkRunInTerminal.setDisplayedMnemonicIndex(7); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 4; gbc.anchor = GridBagConstraints.WEST; panel1.add(chkRunInTerminal, gbc); chkOutputShouldReplaceSelection = new JRadioButton(); chkOutputShouldReplaceSelection.setText("Output should replace selection"); chkOutputShouldReplaceSelection.setMnemonic('R'); chkOutputShouldReplaceSelection.setDisplayedMnemonicIndex(14); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 5; gbc.anchor = GridBagConstraints.WEST; panel1.add(chkOutputShouldReplaceSelection, gbc); chkShowPreviewPriorToExecution = new JCheckBox(); chkShowPreviewPriorToExecution.setSelected(true); chkShowPreviewPriorToExecution.setText("Show preview prior to execution"); chkShowPreviewPriorToExecution.setMnemonic('P'); chkShowPreviewPriorToExecution.setDisplayedMnemonicIndex(5); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 6; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(5, 2, 2, 2); panel1.add(chkShowPreviewPriorToExecution, gbc); txtGroup = new JTextField(); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 2; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(2, 2, 2, 2); panel1.add(txtGroup, gbc); txtName = new JTextField(); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 1.0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(2, 2, 2, 2); panel1.add(txtName, gbc); chkRunInBackground = new JRadioButton(); chkRunInBackground.setSelected(true); chkRunInBackground.setText("Run in background"); chkRunInBackground.setMnemonic('B'); chkRunInBackground.setDisplayedMnemonicIndex(7); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 3; gbc.anchor = GridBagConstraints.WEST; panel1.add(chkRunInBackground, gbc); final JPanel panel3 = new JPanel(); panel3.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); formPanel.add(panel3, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); btnCancel = new JButton(); btnCancel.setText("Cancel"); btnCancel.setMnemonic('C'); btnCancel.setDisplayedMnemonicIndex(0); panel3.add(btnCancel, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnOk = new JButton(); btnOk.setText("Ok"); btnOk.setMnemonic('O'); btnOk.setDisplayedMnemonicIndex(0); panel3.add(btnOk, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final Spacer spacer1 = new Spacer(); panel3.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); btnAdvanced = new JButton(); btnAdvanced.setText("Advanced"); btnAdvanced.setMnemonic('A'); btnAdvanced.setDisplayedMnemonicIndex(0); panel3.add(btnAdvanced, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); label2.setLabelFor(txtName); label3.setLabelFor(txtCommand); label4.setLabelFor(txtGroup); ButtonGroup buttonGroup; buttonGroup = new ButtonGroup(); buttonGroup.add(chkRunInTerminal); buttonGroup.add(chkOutputShouldReplaceSelection); buttonGroup.add(chkRunInBackground); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return formPanel; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenu.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import burp.IContextMenuFactory; import burp.IContextMenuInvocation; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.Context; import net.bytebutcher.burpsendtoextension.models.Placeholders; import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; import javax.swing.*; import java.util.HashMap; import java.util.List; import java.util.Map; public class SendToContextMenu implements IContextMenuFactory { private BurpExtender burpExtender; private SendToTableListener sendToTableListener; public SendToContextMenu(BurpExtender burpExtender, SendToTableListener sendToTableListener) { this.burpExtender = burpExtender; this.sendToTableListener = sendToTableListener; } @Override public List createMenuItems(IContextMenuInvocation invocation) { List> placeholders = Placeholders.get(BurpExtender.getCallbacks(), invocation.getSelectedMessages()); List commandObjects = BurpExtender.getConfig().getSendToTableData(); if (commandObjects.isEmpty()) { return Lists.newArrayList(); } JMenu sendToMenu = new JMenu("Send to..."); HashMap> groupedCommandObjects = Maps.newLinkedHashMap(); boolean hasEmptyGroup = false; for (final CommandObject commandObject : commandObjects) { String group = commandObject.getGroup(); if (group.isEmpty()) { addMenuItem(sendToMenu, commandObject, placeholders, invocation); hasEmptyGroup = true; continue; } if (!groupedCommandObjects.containsKey(group)) { groupedCommandObjects.put(group, Lists.newArrayList()); } groupedCommandObjects.get(group).add(commandObject); } if (hasEmptyGroup && !groupedCommandObjects.isEmpty()) { sendToMenu.addSeparator(); } for (String group : groupedCommandObjects.keySet()) { JMenu menuItem = new JMenu(group); for (CommandObject commandObject : groupedCommandObjects.get(group)) { addMenuItem(menuItem, commandObject, placeholders, invocation); } sendToMenu.add(menuItem); } return Lists.newArrayList(sendToMenu); } private void addMenuItem(JMenu menu, CommandObject commandObject, List> placeholders, IContextMenuInvocation invocation) { JMenuItem item; Context context = new Context(invocation); if (commandObject.doesRequireRequestResponse(placeholders.get(0)) && context.getOrigin() == Context.Origin.UNKNOWN) { item = new JMenu(commandObject.getName()); SendToContextMenuItem request = new SendToContextMenuItem("request", commandObject, placeholders, new Context(Context.Origin.HTTP_REQUEST, invocation), sendToTableListener); SendToContextMenuItem response = new SendToContextMenuItem("response", commandObject, placeholders, new Context(Context.Origin.HTTP_RESPONSE, invocation), sendToTableListener); item.add(request); item.add(response); item.setEnabled(request.isEnabled() || response.isEnabled()); } else { item = new SendToContextMenuItem(commandObject.getName(), commandObject, placeholders, context, sendToTableListener); } menu.add(item); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenuItem.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import net.bytebutcher.burpsendtoextension.gui.action.SendToContextMenuItemAction; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.Context; import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; import javax.swing.*; import java.util.List; import java.util.Map; public class SendToContextMenuItem extends JMenuItem { public SendToContextMenuItem(String title, CommandObject commandObject, List> placeholders, Context context, SendToTableListener sendToTableListener) { String text = ""; List> validEntries = commandObject.getValid(placeholders, context); if (placeholders.size() > 1) { text = title + " (" + validEntries.size() + "/" + placeholders.size() + ")"; } else { text = title; } this.setAction(new SendToContextMenuItemAction(text, commandObject, placeholders, sendToTableListener, context)); if (commandObject.shouldOutputReplaceSelection() && context.getSelectionBounds() == null) { // Always disable context menu item, when command should replace selection but no selection was made. this.setEnabled(false); } else { // Do only enable context menu item, when at least one HTTP-message can be used to construct the command. this.setEnabled(validEntries.size() > 0); } } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SendToPreviewDialog { private JTextArea txtCommandPreview; private JButton cancelButton; private JButton okButton; private JCheckBox alwaysShowThisDialogCheckBox; private JPanel formPanel; private SendToTableListener sendToTableListener; private AbstractAction onOkAction; private AbstractAction onCancelAction; private final JDialog dialog; private boolean success = false; public SendToPreviewDialog(JFrame parent, String title, final String command) { this.dialog = initDialog(parent, title); this.txtCommandPreview.setText(command); initEventListener(); initKeyboardShortcuts(); } public SendToPreviewDialog(JFrame parent, String title, final String command, final String commandId, final SendToTableListener sendToTableListener) { this(parent, title, command); this.sendToTableListener = sendToTableListener; initEventListener(commandId, sendToTableListener); } private void initEventListener() { onOkAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { success = true; dialog.dispose(); } }; okButton.addActionListener(onOkAction); onCancelAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { success = false; dialog.dispose(); } }; cancelButton.addActionListener(onCancelAction); } private void initEventListener(final String commandId, final SendToTableListener sendToTableListener) { alwaysShowThisDialogCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sendToTableListener.onShowPreviewChange(e, commandId, alwaysShowThisDialogCheckBox.isSelected()); } }); alwaysShowThisDialogCheckBox.setVisible(true); } private JDialog initDialog(JFrame parent, String title) { JDialog dialog = new JDialog(parent, title, true); dialog.getContentPane().add(this.getRootPanel()); dialog.pack(); dialog.setSize(540, 200); int x = DialogUtil.getX(parent, dialog); int y = DialogUtil.getY(parent, dialog); dialog.setLocation(x, y); return dialog; } private void initKeyboardShortcuts() { bindKeyStrokeToAction("ESCAPE", onCancelAction); bindKeyStrokeToAction("ENTER", onOkAction); } private void bindKeyStrokeToAction(String keyStroke, Action action) { KeyStroke stroke = KeyStroke.getKeyStroke(keyStroke); InputMap inputMap = formPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(stroke, keyStroke); formPanel.getActionMap().put(keyStroke, action); } public boolean run() { this.dialog.setVisible(true); return this.success; } public String getCommand() { return this.txtCommandPreview.getText().replace("\r", ""); } private Component getRootPanel() { return formPanel; } { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< // DO NOT EDIT OR ADD ANY CODE HERE! $$$setupUI$$$(); } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { formPanel = new JPanel(); formPanel.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1)); final JLabel label1 = new JLabel(); label1.setText("Do you really want to execute the following command(s)?"); formPanel.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); formPanel.add(panel1, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final Spacer spacer1 = new Spacer(); panel1.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); cancelButton = new JButton(); cancelButton.setText("Cancel"); cancelButton.setMnemonic('C'); cancelButton.setDisplayedMnemonicIndex(0); panel1.add(cancelButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); okButton = new JButton(); okButton.setText("Ok"); okButton.setMnemonic('O'); okButton.setDisplayedMnemonicIndex(0); panel1.add(okButton, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); alwaysShowThisDialogCheckBox = new JCheckBox(); alwaysShowThisDialogCheckBox.setSelected(true); alwaysShowThisDialogCheckBox.setText("Always show this dialog"); alwaysShowThisDialogCheckBox.setVisible(false); panel1.add(alwaysShowThisDialogCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JScrollPane scrollPane1 = new JScrollPane(); formPanel.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); txtCommandPreview = new JTextArea(); scrollPane1.setViewportView(txtCommandPreview); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return formPanel; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class SendToRunInTerminalBehaviourChoiceDialog extends JDialog { private EChoice choice; public enum EChoice { RUN_IN_SINGLE_TERMINAL, RUN_IN_SEPARATE_TERMINALS, REVIEW_COMMANDS, CANCEL } private final JFrame parent; private JPanel contentPane; private JButton btnRunInSingleTerminal; private JButton btnCancel; private JButton btnRunInSeparateTerminals; private JLabel lblCommandCount; private JButton btnReviewCommands; public SendToRunInTerminalBehaviourChoiceDialog(JFrame parent, ERunInTerminalBehaviour defaultChoice, int nrOfCommands) { this.parent = parent; this.choice = getDefaultChoice(defaultChoice); this.lblCommandCount.setText(String.valueOf(nrOfCommands)); setContentPane(contentPane); setTitle("Select execution behaviour"); setModal(true); btnRunInSeparateTerminals.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SEPARATE_TERMINALS)); btnRunInSingleTerminal.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SINGLE_TERMINAL)); btnReviewCommands.addActionListener(e -> onButtonPress(EChoice.REVIEW_COMMANDS)); btnCancel.addActionListener(e -> onButtonPress(EChoice.CANCEL)); // call onCancel() when cross is clicked setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { onCancel(); } }); // call onCancel() on ESCAPE contentPane.registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent e) { onCancel(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); this.pack(); initButtonState(defaultChoice); } private void initButtonState(ERunInTerminalBehaviour defaultChoice) { switch (defaultChoice) { case RUN_IN_SEPARATE_TERMINALS: SwingUtilities.getRootPane(btnRunInSeparateTerminals).setDefaultButton(btnRunInSeparateTerminals); btnRunInSeparateTerminals.grabFocus(); break; case RUN_IN_SINGLE_TERMINAL: // fall through default: SwingUtilities.getRootPane(btnRunInSingleTerminal).setDefaultButton(btnRunInSingleTerminal); btnRunInSingleTerminal.grabFocus(); } } private EChoice getDefaultChoice(ERunInTerminalBehaviour defaultChoice) { switch (defaultChoice) { case RUN_IN_SEPARATE_TERMINALS: return EChoice.RUN_IN_SEPARATE_TERMINALS; case RUN_IN_SINGLE_TERMINAL: // fall through default: return EChoice.RUN_IN_SINGLE_TERMINAL; } } public EChoice run() { int x = DialogUtil.getX(parent, this); int y = DialogUtil.getY(parent, this); this.setLocation(x, y); this.setVisible(true); return choice; } private void onButtonPress(EChoice choice) { this.choice = choice; dispose(); } private void onCancel() { this.choice = EChoice.CANCEL; dispose(); } { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< // DO NOT EDIT OR ADD ANY CODE HERE! $$$setupUI$$$(); } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { contentPane = new JPanel(); contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayoutManager(1, 6, new Insets(0, 0, 0, 0), -1, -1)); contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); final Spacer spacer1 = new Spacer(); panel1.add(spacer1, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); btnRunInSeparateTerminals = new JButton(); btnRunInSeparateTerminals.setText("Run in separate terminals"); btnRunInSeparateTerminals.setMnemonic('S'); btnRunInSeparateTerminals.setDisplayedMnemonicIndex(7); panel1.add(btnRunInSeparateTerminals, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnCancel = new JButton(); btnCancel.setText("Cancel"); panel1.add(btnCancel, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final Spacer spacer2 = new Spacer(); panel1.add(spacer2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); btnReviewCommands = new JButton(); btnReviewCommands.setText("Review commands"); btnReviewCommands.setMnemonic('O'); btnReviewCommands.setDisplayedMnemonicIndex(8); panel1.add(btnReviewCommands, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnRunInSingleTerminal = new JButton(); btnRunInSingleTerminal.setText("Run in single terminal"); btnRunInSingleTerminal.setMnemonic('I'); btnRunInSingleTerminal.setDisplayedMnemonicIndex(8); panel1.add(btnRunInSingleTerminal, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel2 = new JPanel(); panel2.setLayout(new GridLayoutManager(2, 4, new Insets(0, 0, 0, 0), -1, -1)); contentPane.add(panel2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label1 = new JLabel(); label1.setText("Please select how the commands should be executed."); panel2.add(label1, new GridConstraints(1, 0, 1, 4, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label2 = new JLabel(); label2.setText("You are going to execute"); panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); lblCommandCount = new JLabel(); lblCommandCount.setText("0"); panel2.add(lblCommandCount, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label3 = new JLabel(); label3.setText("commands."); panel2.add(label3, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final Spacer spacer3 = new Spacer(); panel2.add(spacer3, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return contentPane; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTab.form ================================================
================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTab.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import net.bytebutcher.burpsendtoextension.gui.listener.ToolTipActionListener; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.gui.util.WebUtil; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.FontUIResource; import javax.swing.text.StyleContext; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.MalformedURLException; import java.net.URL; import java.util.Locale; public class SendToTab { private BurpExtender burpExtender; private JButton btnRemove; private JButton btnEdit; private JButton btnAdd; private JTable tblSendTo; private SendToTable sendToTable; private JPanel formPanel; private JButton btnUp; private JButton btnDown; private JLabel lblSettings; private JLabel lblHelp; private JTextField txtRunInTerminal; private JButton btnRunInTerminalHelp; private JRadioButton chkRunInSingleTerminal; private JRadioButton chkRunInSeparateTerminals; private JCheckBox chkShowRunInTerminalBehaviourChoiceDialog; private JCheckBox chkSafeMode; private SendToTableListener sendToTableListener; private final SendToTabSettingsContextMenu sendToTabSettingsContextMenu; public SendToTab(final BurpExtender burpExtender) { this.burpExtender = burpExtender; $$$setupUI$$$(); this.lblHelp.setIcon(this.burpExtender.createImageIcon("/panel_help.png", "", 24, 24)); this.lblSettings.setIcon(this.burpExtender.createImageIcon("/panel_settings.png", "", 24, 24)); this.sendToTableListener = new SendToTableListener(this.sendToTable); this.tblSendTo.getModel().addTableModelListener(sendToTableListener); btnAdd.addActionListener(e -> new Thread(() -> { SendToAddDialog addDialog = new SendToAddDialog(getParent(), "Add context menu entry", sendToTable.getCommandObjects()); if (addDialog.run()) { sendToTableListener.onAddButtonClick(e, addDialog.getCommandObject()); } }).start()); btnEdit.addActionListener(e -> { CommandObject selectedCommandObject = sendToTable.getSelectedCommandObject(); SendToAddDialog editDialog = new SendToAddDialog(getParent(), "Edit context menu entry", sendToTable.getCommandObjects(), selectedCommandObject); if (editDialog.run()) { sendToTableListener.onEditButtonClick(e, editDialog.getCommandObject()); } }); btnRemove.addActionListener(e -> { boolean result = DialogUtil.showConfirmationDialog(getParent(), "Delete context menu entries", "Do you really want to delete the selected context menu entries?"); if (result) { sendToTableListener.onRemoveButtonClick(e); } }); btnUp.addActionListener(e -> sendToTableListener.onUpButtonClick(e)); btnDown.addActionListener(e -> sendToTableListener.onDownButtonClick(e)); lblHelp.addMouseListener(new LabelIconImageHoverAdapter(lblHelp, "/panel_help.png", "/panel_help_highlighted.png")); lblSettings.addMouseListener(new LabelIconImageHoverAdapter(lblSettings, "/panel_settings.png", "/panel_settings_highlighted.png")); lblHelp.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { try { WebUtil.openWebpage(new URL("https://github.com/bytebutcher/burp-send-to")); } catch (MalformedURLException e1) { // Nothing to do here... } } }); sendToTabSettingsContextMenu = new SendToTabSettingsContextMenu(burpExtender, this); lblSettings.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { sendToTabSettingsContextMenu.show(lblSettings, lblSettings.getX() + lblSettings.getWidth(), lblSettings.getY()); } }); txtRunInTerminal.setText(BurpExtender.getConfig().getRunInTerminalCommand()); txtRunInTerminal.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { save(); } public void removeUpdate(DocumentEvent e) { save(); } public void insertUpdate(DocumentEvent e) { save(); } public void save() { BurpExtender.getConfig().setRunInTerminalCommand(txtRunInTerminal.getText()); } }); btnRunInTerminalHelp.addActionListener(new ToolTipActionListener(btnRunInTerminalHelp, "" + "" + "

%C = Command

" + "") ); chkRunInSingleTerminal.setSelected(BurpExtender.getConfig().getRunInTerminalBehaviour() == ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL); chkRunInSingleTerminal.addChangeListener(e -> { BurpExtender.getConfig().setRunInTerminalBehaviour(chkRunInSingleTerminal.isSelected() ? ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL : ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS); }); chkRunInSeparateTerminals.setSelected(BurpExtender.getConfig().getRunInTerminalBehaviour() == ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS); chkRunInSeparateTerminals.addChangeListener(e -> { BurpExtender.getConfig().setRunInTerminalBehaviour(chkRunInSeparateTerminals.isSelected() ? ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS : ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL); }); chkShowRunInTerminalBehaviourChoiceDialog.setSelected(BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog()); chkShowRunInTerminalBehaviourChoiceDialog.addChangeListener(e -> BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog(chkShowRunInTerminalBehaviourChoiceDialog.isSelected())); this.chkSafeMode.setSelected(BurpExtender.getConfig().isSafeModeActivated()); this.chkSafeMode.addChangeListener(e -> BurpExtender.getConfig().setSafeMode(this.chkSafeMode.isSelected())); } public void resetOptions() { resetSendToTableData(); resetRunInTerminalOption(); } private void resetSendToTableData() { sendToTable.clearTable(); sendToTable.addCommandObjects(BurpExtender.getConfig().getDefaultSendToTableData()); } private void resetRunInTerminalOption() { BurpExtender.getConfig().resetRunInTerminalCommand(); txtRunInTerminal.setText(BurpExtender.getConfig().getRunInTerminalCommand()); } /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< * DO NOT edit this method OR call it in your code! * * @noinspection ALL */ private void $$$setupUI$$$() { createUIComponents(); formPanel = new JPanel(); formPanel.setLayout(new GridLayoutManager(5, 1, new Insets(10, 10, 10, 10), -1, -1)); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 0, 0), -1, -1)); formPanel.add(panel1, new GridConstraints(0, 0, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JPanel panel2 = new JPanel(); panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); panel1.add(panel2, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JPanel panel3 = new JPanel(); panel3.setLayout(new GridLayoutManager(6, 1, new Insets(0, 0, 0, 0), -1, -1)); panel2.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); btnRemove = new JButton(); btnRemove.setText("Remove"); panel3.add(btnRemove, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnEdit = new JButton(); btnEdit.setText("Edit"); panel3.add(btnEdit, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnAdd = new JButton(); btnAdd.setText("Add"); panel3.add(btnAdd, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final Spacer spacer1 = new Spacer(); panel3.add(spacer1, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); btnUp = new JButton(); btnUp.setText("Up"); panel3.add(btnUp, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); btnDown = new JButton(); btnDown.setText("Down"); panel3.add(btnDown, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JScrollPane scrollPane1 = new JScrollPane(); panel2.add(scrollPane1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); scrollPane1.setViewportView(tblSendTo); final JLabel label1 = new JLabel(); label1.setText("Manage entries of the \"Send to...\" context menu."); panel1.add(label1, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label2 = new JLabel(); Font label2Font = this.$$$getFont$$$("Tahoma", Font.BOLD, 14, label2.getFont()); if (label2Font != null) label2.setFont(label2Font); label2.setForeground(new Color(-1341440)); label2.setText("Context Menu Entries"); panel1.add(label2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel4 = new JPanel(); panel4.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1)); panel1.add(panel4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, new Dimension(26, 26), 0, false)); lblSettings = new JLabel(); lblSettings.setText(""); panel4.add(lblSettings, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel5 = new JPanel(); panel5.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1)); panel1.add(panel5, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, new Dimension(26, 26), 0, false)); lblHelp = new JLabel(); lblHelp.setText(""); panel5.add(lblHelp, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel6 = new JPanel(); panel6.setLayout(new GridLayoutManager(8, 2, new Insets(0, 0, 0, 0), -1, -1)); formPanel.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label3 = new JLabel(); Font label3Font = this.$$$getFont$$$("Tahoma", Font.BOLD, 14, label3.getFont()); if (label3Font != null) label3.setFont(label3Font); label3.setForeground(new Color(-1341440)); label3.setText("Terminal Options"); panel6.add(label3, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel7 = new JPanel(); panel7.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1)); panel6.add(panel7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(26, 26), null, new Dimension(26, 26), 0, false)); final JLabel label4 = new JLabel(); label4.setText(""); panel7.add(label4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label5 = new JLabel(); label5.setText("When multiple commands are going to be executed at once"); panel6.add(label5, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label6 = new JLabel(); label6.setText("Specify how to run commands in terminal:"); panel6.add(label6, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel8 = new JPanel(); panel8.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); panel6.add(panel8, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); txtRunInTerminal = new JTextField(); txtRunInTerminal.setText("/bin/bash -c {CMD}"); panel8.add(txtRunInTerminal, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); btnRunInTerminalHelp = new JButton(); btnRunInTerminalHelp.setText("?"); panel8.add(btnRunInTerminalHelp, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); chkShowRunInTerminalBehaviourChoiceDialog = new JCheckBox(); chkShowRunInTerminalBehaviourChoiceDialog.setText("Show dialog to select execution behaviour when multiple commands are going to be executed"); panel6.add(chkShowRunInTerminalBehaviourChoiceDialog, new GridConstraints(6, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel9 = new JPanel(); panel9.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); panel6.add(panel9, new GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); chkRunInSingleTerminal = new JRadioButton(); chkRunInSingleTerminal.setText("execute commands sequential in single terminal"); panel9.add(chkRunInSingleTerminal, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label7 = new JLabel(); label7.setText(" "); panel9.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JPanel panel10 = new JPanel(); panel10.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); panel6.add(panel10, new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); chkRunInSeparateTerminals = new JRadioButton(); chkRunInSeparateTerminals.setText("execute commands in parallel in separate terminals"); panel10.add(chkRunInSeparateTerminals, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final JLabel label8 = new JLabel(); label8.setText(" "); panel10.add(label8, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); chkSafeMode = new JCheckBox(); chkSafeMode.setSelected(true); chkSafeMode.setText("Surround placeholders with single quotes automatically (safe mode)"); panel6.add(chkSafeMode, new GridConstraints(7, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); final Spacer spacer2 = new Spacer(); formPanel.add(spacer2, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); ButtonGroup buttonGroup; buttonGroup = new ButtonGroup(); buttonGroup.add(chkRunInSeparateTerminals); buttonGroup.add(chkRunInSeparateTerminals); buttonGroup.add(chkRunInSingleTerminal); } /** * @noinspection ALL */ private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) { if (currentFont == null) return null; String resultName; if (fontName == null) { resultName = currentFont.getName(); } else { Font testFont = new Font(fontName, Font.PLAIN, 10); if (testFont.canDisplay('a') && testFont.canDisplay('1')) { resultName = fontName; } else { resultName = currentFont.getName(); } } Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize()); boolean isMac = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).startsWith("mac"); Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize()); return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback); } /** * @noinspection ALL */ public JComponent $$$getRootComponent$$$() { return formPanel; } class LabelIconImageHoverAdapter extends MouseAdapter { private String resource; private String resourceHovered; private JLabel label; public LabelIconImageHoverAdapter(JLabel label, String resource, String resourceHovered) { this.label = label; this.resource = resource; this.resourceHovered = resourceHovered; } @Override public void mouseEntered(MouseEvent e) { label.setIcon(SendToTab.this.burpExtender.createImageIcon(resourceHovered, "", 24, 24)); } @Override public void mouseExited(MouseEvent e) { label.setIcon(SendToTab.this.burpExtender.createImageIcon(resource, "", 24, 24)); } } public JPanel getRootPanel() { return formPanel; } public JFrame getParent() { return (JFrame) SwingUtilities.getRootPane(this.getRootPanel()).getParent(); } public SendToTable getSendToTable() { return sendToTable; } /** * Creates Custom GUI forms */ private void createUIComponents() { this.tblSendTo = this.sendToTable = new SendToTable(this.burpExtender); } public SendToTableListener getSendToTableListener() { return this.sendToTableListener; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTabSettingsContextMenu.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.models.CommandObject; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; class SendToTabSettingsContextMenu extends JPopupMenu { private BurpExtender burpExtender; private final JMenuItem restoreDefaults; private final JMenuItem loadOptions; private final JMenuItem saveOptions; private SendToTable sendToTable; private SendToTab sendToTab; public SendToTabSettingsContextMenu(final BurpExtender burpExtender, final SendToTab sendToTab) { this.burpExtender = burpExtender; this.sendToTab = sendToTab; this.sendToTable = sendToTab.getSendToTable(); restoreDefaults = new JMenuItem("Restore defaults"); restoreDefaults.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean result = DialogUtil.showConfirmationDialog(sendToTab.getParent(), "Reset \"Send to\"-options", "Do you really want to reset the \"Send to\"-options?"); if (result) { sendToTab.resetOptions(); } } }); add(restoreDefaults); loadOptions = new JMenuItem("Load options"); loadOptions.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { burpExtender.getCallbacks().printOutput("Loading options..."); JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Load \"Send to\" options from file..."); fileChooser.setCurrentDirectory(new File(System.getProperty("user.home"))); int result = fileChooser.showOpenDialog(getParent()); if (result == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); try { burpExtender.getCallbacks().printOutput("Reading selected file: " + selectedFile.getAbsolutePath()); List commandObjectList = new Gson().fromJson(new FileReader(selectedFile), new TypeToken>(){}.getType()); burpExtender.getCallbacks().printOutput("Adding " + commandObjectList.size() + " items to table..."); sendToTable.removeAll(); sendToTable.addCommandObjects(commandObjectList); burpExtender.getCallbacks().printOutput("Successfully loaded options into table!"); } catch (FileNotFoundException e1) { DialogUtil.showErrorDialog( sendToTab.getParent(), "Error while loading options!", "

There was an unknown error while loading the options!

" + "

For more information check out the \"Send to\" extension error log!

" ); burpExtender.getCallbacks().printError("Error while loading options: " + e1); return; } catch (Exception e2) { burpExtender.getCallbacks().printError("Error while loading options: " + e2); return; } } } }); add(loadOptions); saveOptions = new JMenuItem("Save options"); saveOptions.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Save \"Send to\" options to file..."); int userSelection = fileChooser.showSaveDialog(getParent()); if (userSelection == JFileChooser.APPROVE_OPTION) { File fileToSave = fileChooser.getSelectedFile(); String json = new Gson().toJson(sendToTable.getCommandObjects()); try (PrintWriter out = new PrintWriter(fileToSave)) { out.write(json); } catch (FileNotFoundException e1) { DialogUtil.showErrorDialog( sendToTab.getParent(), "Error while saving options!", "

There was an unknown error while saving the options!

" + "

For more information check out the \"Send to\" extension error log!

" ); burpExtender.getCallbacks().printError("Error while saving options: " + e1); return; } burpExtender.getCallbacks().printOutput("Successfully saved options in '" + fileToSave.getAbsolutePath() + "'!"); } } }); add(saveOptions); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTable.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import com.google.common.collect.Lists; import com.google.common.primitives.Ints; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.ERuntimeBehaviour; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.table.DefaultTableModel; import java.util.*; import java.util.stream.Collectors; public class SendToTable extends JTable { private final DefaultTableModel defaultModel; private BurpExtender burpExtender; private enum Column { ID(0), NAME(1), COMMAND(2), GROUP(3), RUNTIME_BEHAVIOUR(4), SHOW_PREVIEW(5), PLACEHOLDERS(6); private final int index; Column(int id) { this.index = id; } public int getIndex() { return index; } } private class SendToTableModel extends DefaultTableModel { private boolean areEventsBlocked; @Override public boolean isCellEditable(int row, int column) { return false; } @Override public void fireTableChanged(TableModelEvent e) { if (!areEventsBlocked) { super.fireTableChanged(e); } } public void setBlockEvents(boolean areEventsBlocked) { this.areEventsBlocked = areEventsBlocked; } } public SendToTable(BurpExtender burpExtender) { this.burpExtender = burpExtender; this.defaultModel = new SendToTableModel(); this.defaultModel.addColumn("Id"); this.defaultModel.addColumn("Name"); this.defaultModel.addColumn("Command"); this.defaultModel.addColumn("Group name"); this.defaultModel.addColumn("Runtime behaviour"); this.defaultModel.addColumn("Show preview"); this.defaultModel.addColumn("Placeholder behaviour"); setModel(this.defaultModel); hideColumns(Column.ID, Column.COMMAND, Column.PLACEHOLDERS); } private void hideColumns(Column ... c) { List collect = Arrays.stream(c).sorted(Comparator.comparingInt(Column::getIndex).reversed()).collect(Collectors.toList()); for (Column column : collect) { this.removeColumn(this.getColumnModel().getColumn(column.getIndex())); } } public CommandObject getSelectedCommandObject() { int[] selectedRows = this.getSelectedRows(); if (selectedRows.length > 0) { int selectedRow = selectedRows[0]; return getCommandObjectByRowIndex(selectedRow); } throw new IllegalStateException("No row selected!"); } public DefaultTableModel getDefaultModel() { return defaultModel; } public String getSelectedNames() { int[] selectedRows = this.getSelectedRows(); if (selectedRows.length > 0) { int selectedRow = selectedRows[0]; return getNameByRowIndex(selectedRow); } throw new IllegalStateException("No row selected!"); } private String getNameByRowIndex(int rowIndex) { return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.NAME.getIndex())).orElse("").toString(); } private boolean getShowPreviewByRowIndex(int rowIndex) { return Boolean.parseBoolean(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.SHOW_PREVIEW.getIndex())).orElse("").toString()); } private ERuntimeBehaviour getRuntimeBehaviourByRowIndex(int rowIndex) { return ERuntimeBehaviour.getEnum(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex())).orElse("").toString()); } private String getGroupByRowIndex(int rowIndex) { return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.GROUP.getIndex())).orElse("").toString(); } private String getCommandByRowIndex(int rowIndex) { return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.COMMAND.getIndex())).orElse("").toString(); } private List getPlaceholders(int rowIndex) { try { return Lists.newArrayList((List) this.getModel().getValueAt(rowIndex, Column.PLACEHOLDERS.getIndex())); } catch (Exception e) { BurpExtender.printErr("Casting of placeholder behaviour failed for row " + rowIndex); return Lists.newArrayList(); } } public List getCommandObjects() { List commandObjects = Lists.newArrayList(); for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { commandObjects.add(getCommandObjectByRowIndex(i)); } return commandObjects; } private CommandObject getCommandObjectByRowIndex(int rowIndex) { String id = this.getModel().getValueAt(rowIndex, Column.ID.getIndex()).toString(); String name = getNameByRowIndex(rowIndex); String command = getCommandByRowIndex(rowIndex); String group = getGroupByRowIndex(rowIndex); ERuntimeBehaviour runtimeBehaviour = getRuntimeBehaviourByRowIndex(rowIndex); boolean showPreview = getShowPreviewByRowIndex(rowIndex); List placeholders = getPlaceholders(rowIndex); return new CommandObject(id, name, command, group, runtimeBehaviour, showPreview, placeholders); } public CommandObject getCommandObjectById(String commandId) { if (commandId == null) { BurpExtender.printErr("CommandObject id should not be null!"); throw new IllegalArgumentException("CommandObject id should not be null!"); } for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { CommandObject commandObject = getCommandObjectByRowIndex(i); if (commandId.equals(commandObject.getId())) { return commandObject; } } BurpExtender.printErr("No command found with the specified id!"); throw new IllegalStateException("No command found with the specified id!"); } public void addCommandObjects(List commandObjectList) { for (CommandObject commandObject : commandObjectList) { addCommandObject(commandObject); } } public void addCommandObject(CommandObject commandObject) { getDefaultModel().addRow(new Object[]{ commandObject.getId(), commandObject.getName(), commandObject.getFormat(), commandObject.getGroup(), commandObject.getRuntimeBehaviour().alternateName(), commandObject.shouldShowPreview(), commandObject.getPlaceholders() }); } public void editSelectedCommandObject(CommandObject commandObject) { int selectedRowIndex = this.getSelectedRow(); if (selectedRowIndex >= 0) { editRow(selectedRowIndex, commandObject); } } private void editRow(int rowIndex, CommandObject commandObject) { DefaultTableModel model = getDefaultModel(); ((SendToTableModel) getDefaultModel()).setBlockEvents(true); model.setValueAt(commandObject.getId(), rowIndex, Column.ID.getIndex()); model.setValueAt(commandObject.getName(), rowIndex, Column.NAME.getIndex()); model.setValueAt(commandObject.getGroup(), rowIndex, Column.GROUP.getIndex()); model.setValueAt(commandObject.getFormat(), rowIndex, Column.COMMAND.getIndex()); model.setValueAt(commandObject.getRuntimeBehaviour().alternateName(), rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex()); model.setValueAt(commandObject.shouldShowPreview(), rowIndex, Column.SHOW_PREVIEW.getIndex()); model.setValueAt(commandObject.getPlaceholders(), rowIndex, Column.PLACEHOLDERS.getIndex()); ((SendToTableModel) getDefaultModel()).setBlockEvents(false); getDefaultModel().fireTableDataChanged(); } public void editCommandObject(CommandObject commandObject) { for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { CommandObject commandObjectFromRow = getCommandObjectByRowIndex(i); if (commandObjectFromRow.getId().equals(commandObject.getId())) { editRow(i, commandObject); return; } } } public void removeSelectedRow() { List rows = Ints.asList(this.getSelectedRows()); Collections.sort(rows, Collections.reverseOrder()); for (Integer row : rows) { getDefaultModel().removeRow(row); } } public void clearTable() { for (int row = this.getRowCount() - 1; row >= 0; row--) { getDefaultModel().removeRow(row); } } public void moveSelectedRowUp() { moveRowBy(-1); } public void moveSelectedRowDown() { moveRowBy(1); } private void moveRowBy(int index) { DefaultTableModel model = (DefaultTableModel) this.getModel(); int[] rows = this.getSelectedRows(); int destination = rows[0] + index; int rowCount = model.getRowCount(); if (destination < 0 || destination >= rowCount) { return; } model.moveRow(rows[0], rows[rows.length - 1], destination); this.setRowSelectionInterval(rows[0] + index, rows[rows.length - 1] + index); } @Override public void removeAll() { DefaultTableModel model = (DefaultTableModel) this.getModel(); for (int i = 0; i < model.getRowCount(); i++) { model.removeRow(i); } } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTableListener.java ================================================ package net.bytebutcher.burpsendtoextension.gui; import burp.BurpExtender; import net.bytebutcher.burpsendtoextension.models.CommandObject; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; public class SendToTableListener implements TableModelListener { private SendToTable sendToTable; public SendToTableListener(SendToTable sendToTable) { this.sendToTable = sendToTable; } @Override public void tableChanged(TableModelEvent e) { BurpExtender.getConfig().saveSendToTableData(sendToTable.getCommandObjects()); } public void onAddButtonClick(ActionEvent e, CommandObject commandObject) { sendToTable.addCommandObject(commandObject); } public void onEditButtonClick(ActionEvent e, CommandObject commandObject) { sendToTable.editSelectedCommandObject(commandObject); } public void onRemoveButtonClick(ActionEvent e) { sendToTable.removeSelectedRow(); } public void onUpButtonClick(ActionEvent e) { sendToTable.moveSelectedRowUp(); } public void onDownButtonClick(ActionEvent e) { sendToTable.moveSelectedRowDown(); } public void onShowPreviewChange(ActionEvent e, String commandId, boolean showPreview) { CommandObject commandObject = sendToTable.getCommandObjectById(commandId); commandObject.setShowPreview(showPreview); sendToTable.editCommandObject(commandObject); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/action/SendToContextMenuItemAction.java ================================================ package net.bytebutcher.burpsendtoextension.gui.action; import burp.BurpExtender; import net.bytebutcher.burpsendtoextension.builder.CommandBuilder; import net.bytebutcher.burpsendtoextension.executioner.CommandExecutioner; import net.bytebutcher.burpsendtoextension.gui.SendToPreviewDialog; import net.bytebutcher.burpsendtoextension.gui.SendToRunInTerminalBehaviourChoiceDialog; import net.bytebutcher.burpsendtoextension.gui.SendToTableListener; import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; import net.bytebutcher.burpsendtoextension.models.CommandObject; import net.bytebutcher.burpsendtoextension.models.Context; import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; import javax.swing.*; import java.awt.event.ActionEvent; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; import java.util.Map; public class SendToContextMenuItemAction extends AbstractAction { private final CommandObject commandObject; private final List> placeholders; private final SendToTableListener sendToTableListener; private final Context context; public SendToContextMenuItemAction(String title, CommandObject commandObject, List> placeholders, SendToTableListener sendToTableListener, Context context) { super(title); this.commandObject = commandObject; this.placeholders = placeholders; this.sendToTableListener = sendToTableListener; this.context = context; } @Override public void actionPerformed(ActionEvent e) { try { String command = new CommandBuilder(commandObject, placeholders, context).build(); if (commandObject.shouldShowPreview()) { command = showSendToPreviewDialog(commandObject.getId(), command); } if (command == null) { return; } if (commandObject.shouldRunInTerminal()) { runCommandInTerminal(command); } else { runCommandInBackground(command); } } catch (Exception e1) { DialogUtil.showErrorDialog( BurpExtender.getParent(), "Error during command execution!", "

There was an unknown error during command execution!

" + "

For more information check out the \"Send to\" extension error log!

" ); BurpExtender.printErr("Error during command execution: " + e1); BurpExtender.printErr(stackTraceToString(e1)); } } private void runCommandInBackground(String command) throws Exception { new CommandExecutioner(commandObject.shouldOutputReplaceSelection(), context).execute(command); } private void runCommandInTerminal(String command) throws Exception { ERunInTerminalBehaviour runInTerminalBehaviour = BurpExtender.getConfig().getRunInTerminalBehaviour(); boolean containsMultipleCommands = command.contains("\n"); if (containsMultipleCommands && BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog()) { SendToRunInTerminalBehaviourChoiceDialog.EChoice choice = null; while (choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SEPARATE_TERMINALS && choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SINGLE_TERMINAL) { choice = new SendToRunInTerminalBehaviourChoiceDialog(BurpExtender.getParent(), runInTerminalBehaviour, command.split("\n").length).run(); switch (choice) { case RUN_IN_SINGLE_TERMINAL: runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL; break; case RUN_IN_SEPARATE_TERMINALS: runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS; break; case REVIEW_COMMANDS: command = showSendToPreviewDialog(command); if (command == null) { return; } case CANCEL: return; } } } new CommandExecutioner(runInTerminalBehaviour, commandObject.shouldOutputReplaceSelection(), context).execute(command); } private String showSendToPreviewDialog(String command) { SendToPreviewDialog previewDialog = new SendToPreviewDialog(BurpExtender.getParent(), "Review commands", command); return previewDialog.run() ? previewDialog.getCommand() : null; } private String showSendToPreviewDialog(String id, String command) throws Exception { SendToPreviewDialog previewDialog = new SendToPreviewDialog( BurpExtender.getParent(), "Execute command?", command, id, sendToTableListener ); if (!previewDialog.run()) { return null; } return previewDialog.getCommand(); } private String stackTraceToString(Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); return sw.toString(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/listener/ToolTipActionListener.java ================================================ package net.bytebutcher.burpsendtoextension.gui.listener; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class ToolTipActionListener implements ActionListener { private JComponent component; private String toolTipText; public ToolTipActionListener(JComponent component, String toolTipText) { this.component = component; this.toolTipText = toolTipText; } @Override public void actionPerformed(ActionEvent e) { JToolTip toolTip = this.component.createToolTip(); toolTip.setTipText(this.toolTipText); PopupFactory popupFactory = PopupFactory.getSharedInstance(); int x = this.component.getLocationOnScreen().x; int y = this.component.getLocationOnScreen().y; x += this.component.getWidth() / 2; y += this.component.getHeight(); final Popup tooltipContainer = popupFactory.getPopup(this.component, toolTip, x, y); tooltipContainer.show(); (new Thread(new Runnable() { public void run() { try { Thread.sleep(10000); } catch (InterruptedException ex) { // Nothing to do here. } tooltipContainer.hide(); } })).start(); } public String getToolTipText() { return toolTipText; } public void setToolTipText(String toolTipText) { this.toolTipText = toolTipText; } public JComponent getComponent() { return component; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/DialogUtil.java ================================================ package net.bytebutcher.burpsendtoextension.gui.util; import javax.swing.*; import java.awt.*; public final class DialogUtil { public static void showErrorDialog(Component parent, String title, String message) { final JDialog dlgError = new JOptionPane(message, JOptionPane.ERROR_MESSAGE).createDialog(parent, title); dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError)); dlgError.setVisible(true); } public static boolean showConfirmationDialog(Component parent, String title, String message) { JOptionPane jOptionPane = new JOptionPane(message, JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION); final JDialog dlgError = jOptionPane.createDialog(parent, title); dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError)); dlgError.setVisible(true); return ((Integer) jOptionPane.getValue()).intValue() == JOptionPane.YES_OPTION; } public static int getX(Component parent, Component child) { try { return parent.getX() + (parent.getWidth() / 2) - (child.getWidth() / 2); } catch (NullPointerException e) { return 0; } } public static int getY(Component parent, Component child) { try { return parent.getY() + (parent.getHeight() / 2) - (child.getHeight() / 2); } catch (NullPointerException e) { return 0; } } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/SelectionUtil.java ================================================ package net.bytebutcher.burpsendtoextension.gui.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; public class SelectionUtil { public static byte[] replaceSelectedText(byte[] message, int[] bounds, String replaceText) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(Arrays.copyOfRange(message, 0, bounds[0])); outputStream.write(replaceText.getBytes()); outputStream.write(Arrays.copyOfRange(message, bounds[1], message.length)); outputStream.flush(); return outputStream.toByteArray(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/WebUtil.java ================================================ package net.bytebutcher.burpsendtoextension.gui.util; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; public class WebUtil { public static boolean openWebpage(URI uri) { Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { try { desktop.browse(uri); return true; } catch (Exception e) { // Nothing to do here... } } return false; } public static boolean openWebpage(URL url) { try { return openWebpage(url.toURI()); } catch (URISyntaxException e) { // Nothing to do here... } return false; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/CommandObject.java ================================================ package net.bytebutcher.burpsendtoextension.models; import burp.BurpExtender; import com.google.common.collect.Lists; import com.google.gson.annotations.SerializedName; import net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponseInfoPlaceholder; import net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponsePlaceholder; import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; import java.util.*; public class CommandObject { private String id = UUID.randomUUID().toString(); private String name; @SerializedName(value="format", alternate={"command"}) // Changed field name from "command" to "format" in version 1.1 private String format; private String group; private ERuntimeBehaviour runtimeBehaviour; private boolean showPreview; private List placeholders; public static class Placeholder { // The name of the placeholder (e.g. "%U"). private final String name; // Defines at which position the placeholder is found in the format string. private final int start; private final int end; // The behavior of the placeholder. private IPlaceholderBehaviour behaviour; public Placeholder(String placeholder, IPlaceholderBehaviour behaviour, int start, int end) { this.name = placeholder; this.behaviour = behaviour; this.start = start; this.end = end; } public String getName() { return name; } public int getStart() { return start; } public int getEnd() { return end; } @Override public String toString() { return "Placeholder{" + "name='" + name + '\'' + ", start=" + start + ", end=" + end + ", behaviour=" + behaviour + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Placeholder that = (Placeholder) o; return Objects.equals(name, that.name); } @Override public int hashCode() { return Objects.hash(name); } public IPlaceholderBehaviour getBehaviour() { return behaviour; } public void setBehaviour(IPlaceholderBehaviour behaviour) { this.behaviour = behaviour; } } public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List placeholders) { this.name = name; this.format = format; this.group = group; this.runtimeBehaviour = runtimeBehaviour; this.showPreview = showPreview; this.placeholders = initPlaceholders(Lists.newArrayList(placeholders)); } public CommandObject(String id, String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List placeholders) { this(name, format, group, runtimeBehaviour, showPreview, placeholders); this.id = id; } public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview) { this(name, format, group, runtimeBehaviour, showPreview, Lists.newArrayList()); } /** * Each context menu entry has a string format. The string format may contain placeholders. * The user can specify the behaviour of the placeholder which is stored in configuration. * This method links the placeholders and it's behavior. * * This method addresses the problem that the string format might have been changed (e.g. added/removed placeholder) * while the behavior was not. As a result this method need to autodetect whether the stored behavior can still be * applied or whether a default behaviour needs to be set. * * @param storedPlaceholders a (incomplete) list of placeholders. if this list is * empty (e.g. when no placeholder was defined by the user), each * placeholder found in the command is associated with a default placeholder behaviour. * Otherwise the placeholder behaviour in the given list is used. * @return the behaviour of each placeholder. */ private List initPlaceholders(List storedPlaceholders) { // Get the placeholders defined in the format string List actualPlaceholders = Placeholders.get(this.format); for (CommandObject.Placeholder actualPlaceholder : actualPlaceholders) { // Check whether a placeholder behavior exists for this placeholder. // We pick the first one we find which might not always be correct - but we can't do it any other way. Placeholder match = storedPlaceholders.stream().filter(x -> x.getName().equals(actualPlaceholder.getName())).findFirst().orElse(null); if (match != null && match.getBehaviour() != null) { actualPlaceholder.setBehaviour(match.getBehaviour()); } else { actualPlaceholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour()); } storedPlaceholders.remove(match); } return actualPlaceholders; } public String getId() { return this.id; } public String getName() { return this.name; } public String getFormat() { return this.format; } public String getGroup() { return group; } public ERuntimeBehaviour getRuntimeBehaviour() { return this.runtimeBehaviour != null ? this.runtimeBehaviour : ERuntimeBehaviour.RUN_IN_TERMINAL; } public boolean shouldRunInTerminal() { return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_TERMINAL; } public boolean shouldOutputReplaceSelection() { return getRuntimeBehaviour() == ERuntimeBehaviour.OUTPUT_SHOULD_REPLACE_SELECTION; } public boolean shouldRunInBackground() { return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_BACKGROUND; } public void setShowPreview(boolean showPreview) { this.showPreview = showPreview; } public boolean shouldShowPreview() { return this.showPreview; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CommandObject that = (CommandObject) o; return id.equals(that.id); } @Override public int hashCode() { return Objects.hash(id); } public List> getValid(List> placeholders, Context context) { List> validItems = Lists.newArrayList(); for (Map placeholderMap : placeholders) { if (isValid(placeholderMap, context)) { validItems.add(placeholderMap); } } return validItems; } private boolean isValid(Map placeholderMap, Context context) { for (CommandObject.Placeholder placeholder : getPlaceholders()) { if (placeholderMap.containsKey(placeholder.getName())) { if (!placeholderMap.get(placeholder.getName()).isValid(context)) { return false; } } } return true; } public boolean doesRequireRequestResponse(Map placeholderMap) { for (Placeholder placeholder : getPlaceholders()) { if (placeholderMap.containsKey(placeholder.getName())) { if (placeholderMap.get(placeholder.getName()) instanceof AbstractRequestResponseInfoPlaceholder || placeholderMap.get(placeholder) instanceof AbstractRequestResponsePlaceholder) { return true; } } } return false; } public List getPlaceholders() { if (placeholders == null) { // placeholder list might be null, when ContextMenuEntry was loaded from config // and no placeholders were defined. placeholders = initPlaceholders(Lists.newArrayList()); } return Lists.newArrayList(placeholders); } @Override public String toString() { return "CommandObject{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", format='" + format + '\'' + ", group='" + group + '\'' + ", runtimeBehaviour=" + runtimeBehaviour + ", showPreview=" + showPreview + ", placeholders=" + placeholders + '}'; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Config.java ================================================ package net.bytebutcher.burpsendtoextension.models; import burp.BurpExtender; import burp.IBurpExtenderCallbacks; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; import net.bytebutcher.burpsendtoextension.utils.OsUtils; import java.util.List; public class Config { private final IBurpExtenderCallbacks callbacks; private final Gson gson; private BurpExtender burpExtender; private String version = "1.6"; public Config(BurpExtender burpExtender) { this.burpExtender = burpExtender; this.callbacks = BurpExtender.getCallbacks(); this.gson = initGson(); refreshVersion(); } private Gson initGson() { RuntimeTypeAdapterFactory placeholderBehaviourAdapterFactory = RuntimeTypeAdapterFactory.of(IPlaceholderBehaviour.class, "type") .registerSubtype(StringSeparatedPlaceholderBehaviour.class, "StringSeparated") .registerSubtype(CommandSeparatedPlaceholderBehaviour.class, "CommandSeparated") .registerSubtype(FileSeparatedPlaceholderBehaviour.class, "FileSeparated"); return new GsonBuilder().registerTypeAdapterFactory(placeholderBehaviourAdapterFactory).create(); } public void saveSendToTableData(List sendToTableData) { this.callbacks.saveExtensionSetting("SendToTableData", gson.toJson(sendToTableData)); } public List getSendToTableData() { List commandObjectList = Lists.newArrayList(); try { String sendToTableData = this.callbacks.loadExtensionSetting("SendToTableData"); if (sendToTableData == null || sendToTableData.isEmpty() || "[]".equals(sendToTableData)) { if (isFirstStart()) { BurpExtender.printOut("Initializing default table data..."); commandObjectList = initializeDefaultSendToTableData(); } return commandObjectList; } return gson.fromJson(sendToTableData, new TypeToken>() {}.getType()); } catch (Exception e) { BurpExtender.printErr("Error retrieving table data!"); BurpExtender.printErr(e.toString()); return commandObjectList; } } private List initializeDefaultSendToTableData() { List commandObjectList = getDefaultSendToTableData(); saveSendToTableData(commandObjectList); unsetFirstStart(); return commandObjectList; } public List getDefaultSendToTableData() { String groupFuzz = "fuzz"; String groupCMS = "cms"; String groupSQL = "sql"; String groupSSL = "ssl"; String groupOther = "other"; return Lists.newArrayList( // cms new CommandObject("droopescan", "droopescan scan drupal -u %U -t 10", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("mooscan", "mooscan -v --url %U", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("wpscan", "wpscan --url %U --threads 10", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), // fuzz new CommandObject("bfac", "bfac --url %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("gobuster", "gobuster -u %U -s 403,404 -w /usr/share/wfuzz/wordlist/general/common.txt", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("nikto", "nikto %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("wfuzz", "wfuzz -c -w /usr/share/wfuzz/wordlist/general/common.txt --hc 404,403 %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), // sql new CommandObject("sqlmap (GET)", "sqlmap -o -u %U --level=5 --risk=3", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("sqlmap (POST)", "sqlmap -r %R --level=5 --risk=3", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), // ssl new CommandObject("sslscan", "sslscan %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("sslyze", "sslyze --regular %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("testssl", "testssl.sh %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), // other new CommandObject("Host (%H)", "echo %H", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("Port (%P)", "echo %P", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("Protocol (%T)", "echo %T", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("URL (%U)", "echo %U", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("URL-Path (%A)", "echo %A", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("URL-Query (%Q)", "echo %Q", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("Cookies (%C)", "echo %C", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("HTTP-Method (%M)", "echo %M", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("Selected text (%S)", "echo %S", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("Selected text as file (%F)", "echo %F && cat %F", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("HTTP-Request/-Response as file (%R)", "echo %R && cat %R", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("HTTP-Headers as file (%E)", "echo %E && cat %E", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), new CommandObject("HTTP-Body as file (%B)", "echo %B && cat %B", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true) ); } private void refreshVersion() { if (!this.version.equals(this.callbacks.loadExtensionSetting("version"))) { this.callbacks.saveExtensionSetting("version", version); setFirstStart(); } } private boolean isFirstStart() { String isFirstStart = this.callbacks.loadExtensionSetting("isFirstStart"); return isFirstStart == null || "true".equals(isFirstStart); } private void setFirstStart() { this.callbacks.saveExtensionSetting("isFirstStart", null); } private void unsetFirstStart() { this.callbacks.saveExtensionSetting("isFirstStart", "false"); } public void setRunInTerminalBehaviour(ERunInTerminalBehaviour runInTerminalBehaviour) { this.callbacks.saveExtensionSetting("runInTerminalBehaviour", runInTerminalBehaviour.name()); } public ERunInTerminalBehaviour getRunInTerminalBehaviour() { String runInTerminalBehaviour = this.callbacks.loadExtensionSetting("runInTerminalBehaviour"); if (runInTerminalBehaviour == null || runInTerminalBehaviour.isEmpty()) { return ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL; } return ERunInTerminalBehaviour.valueOf(runInTerminalBehaviour); } public boolean shouldShowRunInTerminalBehaviourChoiceDialog() { String showDialog = this.callbacks.loadExtensionSetting("showRunInTerminalBehaviourDialog"); if (showDialog == null || showDialog.isEmpty()) { return true; // Default } return Boolean.parseBoolean(showDialog); } public void shouldShowRunInTerminalBehaviourChoiceDialog(boolean status) { this.callbacks.saveExtensionSetting("showRunInTerminalBehaviourDialog", String.valueOf(status)); } public String getRunInTerminalCommand() { if (OsUtils.isWindows()) { return getRunInTerminalCommand("runInTerminalSettingWindows", "cmd /c start cmd /K %C"); } else { return getRunInTerminalCommand("runInTerminalSettingUnix", "xterm -hold -e %C"); } } private String getRunInTerminalCommand(String label, String defaultValue) { String runInTerminalSetting = this.callbacks.loadExtensionSetting(label); if (runInTerminalSetting == null || runInTerminalSetting.isEmpty()) { runInTerminalSetting = defaultValue; this.callbacks.saveExtensionSetting(label, runInTerminalSetting); } return runInTerminalSetting; } public void setRunInTerminalCommand(String command) { if (OsUtils.isWindows()) { this.callbacks.saveExtensionSetting("runInTerminalSettingWindows", command); } else { this.callbacks.saveExtensionSetting("runInTerminalSettingUnix", command); } } public void resetRunInTerminalCommand() { this.callbacks.saveExtensionSetting("runInTerminalSettingWindows", "cmd /c start cmd /K %C"); this.callbacks.saveExtensionSetting("runInTerminalSettingUnix", "xterm -hold -e %C"); } public void setSafeMode(boolean status) { this.callbacks.saveExtensionSetting("safeMode", String.valueOf(status)); } public boolean isSafeModeActivated() { String safeMode = this.callbacks.loadExtensionSetting("safeMode"); if (safeMode == null || safeMode.isEmpty()) return true; return Boolean.parseBoolean(safeMode); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Context.java ================================================ package net.bytebutcher.burpsendtoextension.models; import burp.IContextMenuInvocation; import burp.IHttpRequestResponse; import com.google.common.collect.Lists; import java.util.ArrayList; public class Context { private final Origin origin; private final IContextMenuInvocation invocation; public enum Origin { HTTP_RESPONSE, HTTP_REQUEST, UNKNOWN; public static Origin getTarget(IContextMenuInvocation contextMenuInvocation) { ArrayList requestContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST); ArrayList responseContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_RESPONSE, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE); if (requestContext.contains(contextMenuInvocation.getInvocationContext())) { return Origin.HTTP_REQUEST; } else if (responseContext.contains(contextMenuInvocation.getInvocationContext())) { return Origin.HTTP_RESPONSE; } else { return Origin.UNKNOWN; } } } public Context(Origin origin, IContextMenuInvocation invocation) { this.origin = origin; this.invocation = invocation; } public Context(IContextMenuInvocation invocation) { this(Origin.getTarget(invocation), invocation); } public Origin getOrigin() { return origin; } /** * NOTE: Access to getSelectedMessages should be restricted, since messages should always be accessed via * RequestResponseHolder. Notably the definition and usage of this function here is a minor design issue. */ public IHttpRequestResponse[] getSelectedMessages() { return invocation.getSelectedMessages(); } public int[] getSelectionBounds() { return invocation.getSelectionBounds(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERunInTerminalBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models; public enum ERunInTerminalBehaviour { RUN_IN_SEPARATE_TERMINALS, RUN_IN_SINGLE_TERMINAL } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERuntimeBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models; public enum ERuntimeBehaviour { RUN_IN_BACKGROUND("Run in Background"), RUN_IN_TERMINAL("Run in Terminal"), OUTPUT_SHOULD_REPLACE_SELECTION("Output should replace Selection"); private String alternateName; ERuntimeBehaviour(String alternateName) { this.alternateName = alternateName; } public String alternateName() { return this.alternateName; } public static ERuntimeBehaviour getEnum(String name) { for (ERuntimeBehaviour value : ERuntimeBehaviour.values()) { if (value.name().equals(name) || value.alternateName().equals(name)) { return value; } } throw new IllegalArgumentException(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Placeholders.java ================================================ package net.bytebutcher.burpsendtoextension.models; import burp.IBurpExtenderCallbacks; import burp.IHttpRequestResponse; import burp.RequestResponseHolder; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.bytebutcher.burpsendtoextension.models.placeholder.*; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class Placeholders { public static List get() { return Lists.newArrayList( new CookiesPlaceholder(), new HostPlaceholder(), new HttpBodyToFilePlaceholder(), new HttpContentLengthPlaceholder(), new HttpHeadersToFilePlaceholder(), new HttpMethodPlaceholder(), new HttpRequestResponsePlaceholder(), new HttpStatusCodePlaceholder(), new PortPlaceholder(), new ProtocolPlaceholder(), new SelectedTextPlaceholder(), new SelectedTextToFilePlaceholder(), new UrlPathPlaceholder(), new UrlPlaceholder(), new UrlQueryPlaceholder()); } /** * Initializes the placeholders for each selected message and returns them in a list. */ public static List> get(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse[] selectedMessages) { List> result = Lists.newArrayList(); if (selectedMessages == null) { return result; } List placeholderList = Placeholders.get(); for (IHttpRequestResponse selectedMessage : selectedMessages) { RequestResponseHolder requestResponseHolder = new RequestResponseHolder(burpExtenderCallbacks, selectedMessage); Map placeholderMap = Maps.newHashMap(); for (AbstractPlaceholder placeholder : placeholderList) { placeholderMap.put(placeholder.getPlaceholder(), placeholder.createParser(requestResponseHolder)); } result.add(placeholderMap); } return result; } public static List get(String format) { List validPlaceholders = Placeholders.get().stream().map(AbstractPlaceholder::getPlaceholder).collect(Collectors.toList()); List placeholders = Lists.newArrayList(); if (format != null && !format.isEmpty()) { Matcher m = Pattern.compile("(\\%[A-Z])").matcher(format); while (m.find()) { String placeholder = m.group(1); if (validPlaceholders.contains(placeholder)) { placeholders.add(new CommandObject.Placeholder(placeholder, null, m.start(), m.end())); } } } return placeholders; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; public abstract class AbstractPlaceholder implements IPlaceholder, IPlaceholderParserFactory { private final String placeholder; private final boolean doesRequireShellEscape; private final boolean shouldWriteToFile; public AbstractPlaceholder(String placeholder, boolean doesRequireShellEscape, boolean shouldWriteToFile) { this.placeholder = placeholder; this.doesRequireShellEscape = doesRequireShellEscape; this.shouldWriteToFile = shouldWriteToFile; } public String getPlaceholder() { return placeholder; } /** * Returns whether the placeholder requires shell-escaping. */ public boolean doesRequireShellEscape() { return doesRequireShellEscape; } public boolean shouldWriteToFile() { return shouldWriteToFile; } public abstract IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder); } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestInfoPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.IRequestInfoWrapper; import burp.RequestResponseHolder; /** * Placeholders which do require additional information from a request. */ public abstract class AbstractRequestInfoPlaceholder extends AbstractRequestResponsePlaceholderBase { private final RequestResponseHolder requestResponseHolder; public AbstractRequestInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { super(placeholder, requestResponseHolder); this.requestResponseHolder = requestResponseHolder; } protected IRequestInfoWrapper getRequestInfo() { return requestResponseHolder.getRequestInfo(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.IHttpRequestResponse; import burp.RequestResponseHolder; public abstract class AbstractRequestPlaceholder extends AbstractRequestResponsePlaceholderBase { private final RequestResponseHolder requestResponseHolder; public AbstractRequestPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { super(placeholder, requestResponseHolder); this.requestResponseHolder = requestResponseHolder; } protected IHttpRequestResponse getHttpRequestResponse() { return requestResponseHolder.getHttpRequestResponse(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponseInfoPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.IRequestInfoWrapper; import burp.IResponseInfoWrapper; import burp.RequestResponseHolder; /** * Placeholder which depends on a request/response is selected/focused. */ public abstract class AbstractRequestResponseInfoPlaceholder extends AbstractRequestResponsePlaceholderBase { private final RequestResponseHolder requestResponseHolder; public AbstractRequestResponseInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { super(placeholder, requestResponseHolder); this.requestResponseHolder = requestResponseHolder; } protected IRequestInfoWrapper getRequestInfo() { return requestResponseHolder.getRequestInfo(); } protected IResponseInfoWrapper getResponseInfo() { return requestResponseHolder.getResponseInfo(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; public abstract class AbstractRequestResponsePlaceholder extends AbstractRequestResponsePlaceholderBase { private final RequestResponseHolder requestResponseHolder; public AbstractRequestResponsePlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { super(placeholder, requestResponseHolder); this.requestResponseHolder = requestResponseHolder; } protected byte[] getRequestResponseAsByteArray(Context context) { switch(context.getOrigin()) { case HTTP_REQUEST: return requestResponseHolder.getHttpRequestResponse().getRequest(); case HTTP_RESPONSE: return requestResponseHolder.getHttpRequestResponse().getResponse(); } return null; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholderBase.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.io.File; import java.io.PrintWriter; import java.util.Optional; public abstract class AbstractRequestResponsePlaceholderBase implements IPlaceholderParser { private final IPlaceholder placeholder; private final RequestResponseHolder requestResponseHolder; public AbstractRequestResponsePlaceholderBase(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { this.placeholder = placeholder; this.requestResponseHolder = requestResponseHolder; } public String getPlaceholder() { return placeholder.getPlaceholder(); } public boolean doesRequireShellEscape() { return placeholder.doesRequireShellEscape(); } public boolean shouldWriteToFile() { return placeholder.shouldWriteToFile(); } /** * Returns the value associated with the placeholder. * * @return the value associated with the placeholder. */ @Nullable protected abstract String getInternalValue(Context context) throws Exception; @Override public String getValue(Context context) throws RuntimeException { try { String value = Optional.ofNullable(getInternalValue(context)).orElse(""); if (placeholder.shouldWriteToFile()) { value = writeToFile(value); } return value; } catch (Exception e) { // This exception is thrown when the placeholder can not be constructed (e.g. no text selected, // no url-query-parameter present, etc.). This is done in favor of returning empty text. // In practice this exception should not be thrown anyway since menu items which contain this placeholder // are disabled and can not be selected anyway (see isValid()). throw new RuntimeException("Error replacing placeholder " + placeholder.getPlaceholder() + " !", e); } } private String writeToFile(String value) throws Exception { try { File tmp = File.createTempFile("burp_", ".snd"); PrintWriter out = new PrintWriter(tmp.getPath()); out.write(value); out.flush(); return tmp.getAbsolutePath(); } catch (RuntimeException e) { throw new Exception(this.getClass().getSimpleName() + ": Error writing to temporary file!", e); } } @Override public boolean isValid(Context context) { try { return getInternalValue(context) != null; } catch (Exception e) { return false; } } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/CookiesPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.ICookie; import burp.RequestResponseHolder; import com.google.common.collect.Lists; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.util.List; import java.util.stream.Collectors; public class CookiesPlaceholder extends AbstractPlaceholder { public CookiesPlaceholder() { super("%C", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { List cookies = Lists.newArrayList(); switch (context.getOrigin()) { case HTTP_REQUEST: cookies = getRequestInfo().getCookies(); break; case HTTP_RESPONSE: cookies = getResponseInfo().getCookies(); break; } return cookies.isEmpty() ? null : cookies.stream().map(iCookie -> iCookie.getName() + "=" + iCookie.getValue()).collect(Collectors.joining(",")); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HostPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HostPlaceholder extends AbstractPlaceholder { public HostPlaceholder() { super("%H", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return getHttpRequestResponse().getHttpService().getHost(); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpBodyToFilePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HttpBodyToFilePlaceholder extends AbstractPlaceholder { public HttpBodyToFilePlaceholder() { super("%B", false, true); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { switch(context.getOrigin()) { case HTTP_REQUEST: return getRequestInfo().getBody(); case HTTP_RESPONSE: return getResponseInfo().getBody(); } return null; } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpContentLengthPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HttpContentLengthPlaceholder extends AbstractPlaceholder { public HttpContentLengthPlaceholder() { super("%L", false, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { switch(context.getOrigin()) { case HTTP_REQUEST: return String.valueOf(getRequestInfo().getBody() == null ? 0 : getRequestInfo().getBody().length()); case HTTP_RESPONSE: return String.valueOf(getResponseInfo().getBody() == null ? 0 : getResponseInfo().getBody().length()); } return null; } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpHeadersToFilePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import com.google.common.collect.Lists; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.util.List; public class HttpHeadersToFilePlaceholder extends AbstractPlaceholder { public HttpHeadersToFilePlaceholder() { super("%E", false, true); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { List headers = Lists.newArrayList(); switch(context.getOrigin()) { case HTTP_REQUEST: headers = getRequestInfo().getHeaders(); break; case HTTP_RESPONSE: headers = getResponseInfo().getHeaders(); break; } return headers.isEmpty() ? null : String.join(System.lineSeparator(), headers); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpMethodPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HttpMethodPlaceholder extends AbstractPlaceholder { public HttpMethodPlaceholder() { super("%M", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return getRequestInfo().getMethod(); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpRequestResponsePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HttpRequestResponsePlaceholder extends AbstractPlaceholder { public HttpRequestResponsePlaceholder() { super("%R", false, true); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { byte[] requestResponse = getRequestResponseAsByteArray(context); return requestResponse != null ? new String(requestResponse) : null; } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpStatusCodePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class HttpStatusCodePlaceholder extends AbstractPlaceholder { public HttpStatusCodePlaceholder() { super("%O", false, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { short statusCode = -1; if (context.getOrigin() == Context.Origin.HTTP_RESPONSE) { statusCode = getResponseInfo().getStatusCode(); } return statusCode == -1 ? null : String.valueOf(statusCode); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; public interface IPlaceholder { String getPlaceholder(); boolean doesRequireShellEscape(); boolean shouldWriteToFile(); } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParser.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import net.bytebutcher.burpsendtoextension.models.Context; public interface IPlaceholderParser extends IPlaceholder { String getValue(Context context) throws RuntimeException; boolean isValid(Context context); } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParserFactory.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; public interface IPlaceholderParserFactory { IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder); } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/PortPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class PortPlaceholder extends AbstractPlaceholder { public PortPlaceholder() { super("%P", false, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return String.valueOf(getHttpRequestResponse().getHttpService().getPort()); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/ProtocolPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class ProtocolPlaceholder extends AbstractPlaceholder { public ProtocolPlaceholder() { super("%T", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return getHttpRequestResponse().getHttpService().getProtocol(); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; public class SelectedTextPlaceholder extends AbstractPlaceholder { public SelectedTextPlaceholder() { super("%S", true, false); } protected SelectedTextPlaceholder(String placeholder, boolean doShellEscape, boolean doWriteToFile) { super(placeholder, doShellEscape, doWriteToFile); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { String value = null; int[] selectionBounds = context.getSelectionBounds(); if (selectionBounds != null) { byte[] requestResponse = getRequestResponseAsByteArray(context); if (requestResponse != null) { boolean isSelectionEmpty = selectionBounds[0] == selectionBounds[1]; if (!isSelectionEmpty) { value = new String(requestResponse).substring(selectionBounds[0], selectionBounds[1]); } } } return value; } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextToFilePlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; public class SelectedTextToFilePlaceholder extends SelectedTextPlaceholder { public SelectedTextToFilePlaceholder() { super("%F", false, true); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPathPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.net.URL; import java.util.Optional; public class UrlPathPlaceholder extends AbstractPlaceholder { public UrlPathPlaceholder() { super("%A", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getPath).orElse(null); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.util.Objects; public class UrlPlaceholder extends AbstractPlaceholder { public UrlPlaceholder() { super("%U", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return Objects.toString(getRequestInfo().getUrl()); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlQueryPlaceholder.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder; import burp.RequestResponseHolder; import net.bytebutcher.burpsendtoextension.models.Context; import javax.annotation.Nullable; import java.net.URL; import java.util.Optional; public class UrlQueryPlaceholder extends AbstractPlaceholder { public UrlQueryPlaceholder() { super("%Q", true, false); } @Override public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { @Nullable @Override protected String getInternalValue(Context context) { return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getQuery).orElse(null); } }; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/CommandSeparatedPlaceholderBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; public class CommandSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/FileSeparatedPlaceholderBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; public class FileSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/IPlaceholderBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; public interface IPlaceholderBehaviour { } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/StringSeparatedPlaceholderBehaviour.java ================================================ package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; public class StringSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { private final String separator; public StringSeparatedPlaceholderBehaviour(String separator) { this.separator = separator; } public String getSeparator() { return separator; } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/OsUtils.java ================================================ package net.bytebutcher.burpsendtoextension.utils; public final class OsUtils { private static String OS = null; public static String getOsName() { if(OS == null) { OS = System.getProperty("os.name"); } return OS; } public static boolean isWindows() { return getOsName().startsWith("Windows"); } public static boolean isUnix() { return !isWindows(); } } ================================================ FILE: burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/StringUtils.java ================================================ package net.bytebutcher.burpsendtoextension.utils; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; import com.google.common.io.CharStreams; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class StringUtils { public static final Escaper shellEscaper; static { final Escapers.Builder builder = Escapers.builder(); builder.addEscape('\'', "'\"'\"'"); shellEscaper = builder.build(); } public static String shellEscape(String command) { return shellEscaper.escape(command); } public static String fromInputStream(InputStream inputStream) throws IOException { return CharStreams.toString(new InputStreamReader(inputStream)); } }