NetsOSS/headless-burp

View on GitHub
burp-maven-plugin/src/main/java/eu/nets/burp/maven/AbstractBurpMojo.java

Summary

Maintainability
A
25 mins
Test Coverage
package eu.nets.burp.maven;

import com.google.common.base.Charsets;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;

public abstract class AbstractBurpMojo extends AbstractMojo {

    private final Log consoleLog = new StreamLog(System.out);

    /**
     * Location of the burpSuite jar.
     */
    @Parameter(required = true)
    private File burpSuite;

    /**
     * Location of the burpSuite project file.
     */
    @Parameter(required = true)
    private File burpProjectFile;

    /**
     * Run headless.
     */
    @Parameter(defaultValue = "true")
    private boolean headless;

    /**
     * Skip starting burp proxy.
     */
    @Parameter(defaultValue = "false")
    private boolean promptOnExit;

    /**
     * Enable verbose output.
     */
    @Parameter(defaultValue = "false")
    private boolean verbose;

    /**
     * Skip starting burp proxy.
     */
    @Parameter(defaultValue = "false")
    private boolean skip;

    /**
     * groupId of the plugin.
     */
    @Parameter(defaultValue = "${plugin.groupId}", readonly = true)
    private String pluginGroupId;

    /**
     * The artifacts for the plugin itself.
     */
    @Parameter(defaultValue = "${plugin.artifacts}", readonly = true)
    private List<Artifact> pluginArtifacts;

    /**
     * Get the name of the Burp tool in scope for this Mojo.
     *
     * @return name of the burp tool
     */
    protected abstract String getBurpExtenderToRun();

    protected abstract boolean shouldWaitForProcessToComplete();

    /**
     * Build Commandline arguments for the burp extension.
     *
     * @return Commandline arguments for the burp extension
     */
    protected abstract List<String> createBurpExtensionCommandLineArguments();

    protected abstract void execute(ProcessBuilder processBuilder);

    @Override
    public void execute() {
        if (skip) {
            getLog().debug("Skipping execution.");
            return;
        }
        if (burpSuite == null || !burpSuite.exists()) {
            getLog().error("Could not find burpSuite jar");
            getLog().debug("Skipping execution.");
            return;
        }

        ProcessBuilder burp = createBurpProcessBuilder();
        execute(burp);
    }

    protected ProcessBuilder createBurpProcessBuilder() {
        List<String> arguments = createBurpExtensionCommandLineArguments();
        arguments.add("--project-file=" + burpProjectFile.getAbsolutePath());
        arguments.add("--unpause-spider-and-scanner");
        if (promptOnExit) {
            arguments.add("-p");
        }
        if (verbose) {
            arguments.add("-v");
        }

        ProcessBuilder builder = new ProcessBuilder("java", "-Xmx1G", "-Djava.awt.headless=" + headless, "-classpath", getBurpExtensionClasspath(), "burp.StartBurp");
        builder.command().addAll(arguments);
        builder.redirectErrorStream(true);

        return builder;
    }

    private String getBurpExtensionClasspath() {
        Utils.ClassPathBuilder builder = Utils.ClassPathBuilder.newInstance();
        builder.path(burpSuite.getAbsolutePath());

        pluginArtifacts.stream()
                .filter(artifact -> Artifact.SCOPE_RUNTIME.equals(artifact.getScope())
                        && pluginGroupId.equals(artifact.getGroupId())
                        && artifact.getArtifactId().contains(getBurpExtenderToRun()))
                .forEach(artifact -> {
                    builder.path(artifact.getFile().getAbsolutePath());
                    getLog().debug("Adding paths artifact: " + artifact);
                });

        return builder.build();
    }

    protected void executeAndRedirectOutput(ProcessBuilder processBuilder) {
        Process process;
        try {
            process = processBuilder.start();
        } catch (IOException e) {
            throw new ProcessExecutionException(e);
        }

        Thread infoLogThread = new StreamLogHandler(process.getInputStream());
        infoLogThread.start();
        Thread errorLogThread = new StreamLogHandler(process.getErrorStream());
        errorLogThread.start();

        if (shouldWaitForProcessToComplete()) {
            try {
                int exitValue = process.waitFor();
                infoLogThread.join();
                errorLogThread.join();

                if (exitValue != 0) {
                    throw new ProcessExecutionException("Error when running " + StringUtils.join(processBuilder.command().iterator(), " "));
                }

            } catch (InterruptedException e) {
                throw new ProcessExecutionException(e);
            }
        }
    }

    private class StreamLogHandler extends Thread {

        private final InputStream inputStream;

        private StreamLogHandler(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public void run() {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charsets.UTF_8));
            String line;

            try {
                while ((line = reader.readLine()) != null) {
                    consoleLog.info(line);
                }
            } catch (IOException e) {
                getLog().error("There was an error reading the output.", e);
            } finally {
                IOUtil.close(inputStream);
            }
        }

    }

    private static class ProcessExecutionException extends RuntimeException {
        ProcessExecutionException(String message) {
            super(message);
        }

        ProcessExecutionException(Throwable cause) {
            super(cause);
        }
    }

}