Skip to content

Commit

Permalink
Initial Zapper commit
Browse files Browse the repository at this point in the history
  • Loading branch information
adedayo committed Jul 15, 2014
0 parents commit f165367
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Zapper is a Jenkins Continuous Integration system plugin that helps you run OWASP ZAP as part of your automated security
assessment regime. The plugin can use a pre-installed version of ZAP when given the path to the ZAP installation.
Alternatively, it can automatically download and build a version of ZAP to be used by your security tests.
70 changes: 70 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.554.3</version>
<!-- which version of Jenkins is this plugin built against? Users must have at least this Jenkins version to use this plugin. -->
</parent>

<developers>
<developer>
<id>dayo</id>
<name>Adedayo Adetoye</name>
<email>dayo.dev@gmail.com</email>
</developer>
</developers>

<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>zapper</artifactId>
<version>1.0.0</version>
<packaging>hpi</packaging>

<licenses>
<license>
<name>MIT License</name>
<url>http://opensource.org/licenses/MIT</url>
</license>
</licenses>

<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>


<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>

<dependencies>
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.3</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<configuration>
<pluginFirstClassLoader>true</pluginFirstClassLoader>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.jenkinsci.plugins.zapper;

import org.kohsuke.stapler.DataBoundConstructor;

/**
* @author Adedayo Adetoye
*/
public class ZapInstallDescription {

private final String type;
private final String path;
private final String repositoryURL;

@DataBoundConstructor
public ZapInstallDescription(String value, String path,
String repositoryURL) {
this.type = value;
this.path = path;
this.repositoryURL = repositoryURL;
}

public String getType() {
return type;
}

public String getPath() {
return path;
}

public String getRepositoryURL() {
return repositoryURL;
}
}
257 changes: 257 additions & 0 deletions src/main/java/org/jenkinsci/plugins/zapper/ZapRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package org.jenkinsci.plugins.zapper;

import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import net.sf.json.JSONObject;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.listener.BigProjectLogger;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.DefaultSVNDebugLogger;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc.admin.SVNAdminEvent;
import org.tmatesoft.svn.core.wc.admin.SVNAdminEventAdapter;
import org.tmatesoft.svn.util.SVNLogType;

import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;

/**
* The Zapper plugin helps to run OWASP ZAP as part of your automated security assessment regime in the Jenkins continuous
* integration system. The plugin can use a pre-installed version of ZAP when given the path to the ZAP installation.
* Alternatively, it can automatically download and build a version of ZAP to be used by your security tests.
*
* @author Adedayo Adetoye
*/
public class ZapRunner extends Builder {

private final String host;
private final ZapInstallDescription zapInstallDescription;
public static final String AUTO = "auto";

@DataBoundConstructor
public ZapRunner(String host, ZapInstallDescription zapInstallDescription) {
this.host = host;
this.zapInstallDescription = zapInstallDescription;
}

public String getHost() {
return host;
}


public String getZapInstallType() {
String t = zapInstallDescription == null ? null : zapInstallDescription.getType();
return t == null ? AUTO : t;
}

public ZapInstallDescription getZapInstallDescription() {
return zapInstallDescription;
}

@Override
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
PrintStream logger = listener.getLogger();
if (AUTO.equals(getZapInstallType())) {

if (!JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_7)) {
logger.println("Zapper cannot find a suitable JDK." +
" You need at least a JDK 7 or above.");
return false;
}

//Checkout ZAP
new File("zapSource").mkdir();
File source = new File("zapSource");
checkout(zapInstallDescription.getRepositoryURL(), source, logger);

//Build ZAP
Project project = new Project();
try {
project.fireBuildStarted();
project.init();
String javaHome = JavaEnvUtils.getJavaHome();
String JAVA_HOME = javaHome.endsWith("/jre") ?
javaHome.substring(0, javaHome.length() - 4) : javaHome;
project.setProperty("java.home", JAVA_HOME);
project.setProperty("build.compiler", "extJavac");//important to set this
project.setProperty("version", "Dev Build");
File buildFile = new File(source, "build/build.xml");
ProjectHelper.configureProject(project, buildFile);
BigProjectLogger projectLogger = new BigProjectLogger();
projectLogger.setOutputPrintStream(logger);
project.addBuildListener(projectLogger);
project.executeTarget("dist");
project.fireBuildFinished(null);
//Now run the newly built ZAP
runZap(source.getAbsolutePath() + "/build/zap", launcher);

} catch (BuildException e) {
e.printStackTrace(logger);
project.fireBuildFinished(e);
return false;
}

} else {
//Use existing Zap installation
String path = zapInstallDescription.getPath();
runZap(path, launcher);
}
return true;
}

private void runZap(String zapPath, Launcher launcher) throws IOException {
String zapExecScript = zapPath + "/zap" + (launcher.isUnix() ? ".sh" : ".bat");
String shell = "/bin/bash";
if (!launcher.isUnix()) shell = "cmd.exe";
String[] hostDesc = host.split(":");
String hostName = hostDesc[0];
String port = hostDesc.length > 1 ? hostDesc[1] : "8090";
Launcher.ProcStarter procStarter = launcher.launch().cmdAsSingleString(shell + " " + zapExecScript +
" -host " + hostName + " -port " + port + " -daemon");
launcher.launch(procStarter);
}


private boolean checkout(String repository, File destination, PrintStream logger) {
logger.println("About to checkout or update ZAP from " + repository);
SVNClientManager cm = SVNClientManager.newInstance();
SVNUpdateClient updateClient = cm.getUpdateClient();
DefaultSVNDebugLogger svnLogger = new DefaultSVNDebugLogger();
svnLogger.createLogStream(SVNLogType.DEFAULT, logger);
updateClient.setDebugLog(svnLogger);
updateClient.setEventHandler(new SVNEventHandler(logger));

System.out.println("About to check out OWASP ZAP from " + zapInstallDescription.getRepositoryURL());
try {
final long revision = updateClient.doCheckout(SVNURL.parseURIEncoded(zapInstallDescription.getRepositoryURL()),
destination, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, false);
logger.println("Finished checkout or update. At revision " + revision);
} catch (SVNException e) {

logger.println("SVN error:");
logger.println(e.getErrorMessage());
SVNErrorCode errorCode = e.getErrorMessage().getErrorCode();
if (SVNErrorCode.WC_CLEANUP_REQUIRED == errorCode
|| SVNErrorCode.WC_LOCKED == errorCode
|| SVNErrorCode.FS_PATH_ALREADY_LOCKED == errorCode) {
SVNWCClient cleaner = new SVNWCClient(new DefaultSVNAuthenticationManager(destination, false, "", ""), new DefaultSVNOptions());
cleaner.setEventHandler(new SVNEventHandler(logger));
cleaner.setDebugLog(svnLogger);
try {
cleaner.doCleanup(destination);
return checkout(repository, destination, logger);
} catch (SVNException e1) {
e1.printStackTrace(logger);
}
}

return false;
}
return false;
}

@Override
public ZapRunnerDescriptor getDescriptor() {
return (ZapRunnerDescriptor) super.getDescriptor();
}

/**
* Descriptor for {@link ZapRunner}. Used as a singleton.
*/
@Extension
public static final class ZapRunnerDescriptor extends BuildStepDescriptor<Builder> {

public ZapRunnerDescriptor() {
load();
}

/**
* Performs on-the-fly validation of the form field 'host'.
*
* @param value This parameter receives the value that the user has typed.
* @return Indicates the outcome of the validation. This is sent to the browser.
* <p/>
* Note that returning {@link FormValidation#error(String)} does not
* prevent the form from being saved. It just means that a message
* will be displayed to the user.
*/
public FormValidation doCheckHost(@QueryParameter("host") String value)
throws IOException, ServletException {
if (value.length() == 0)
return FormValidation.error("Please set a name such as localhost or localhost:8090");
if (value.split(":").length > 2)
return FormValidation.error("Acceptable format include localhost or localhost:8090");
return FormValidation.ok();
}

public boolean isApplicable(Class<? extends AbstractProject> aClass) {
return true;
}

/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "Run OWASP ZAP";
}

@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
save();
return super.configure(req, formData);
}


public String getDefaultHost() {
return "localhost:8090";
}

public String getDefaultRepository() {
return "http://zaproxy.googlecode.com/svn/trunk/";
}

public String getDefaultPath() {
return System.getProperty("user.dir");
}

}
}

class SVNEventHandler extends SVNAdminEventAdapter {
private final PrintStream logger;

public SVNEventHandler(PrintStream logger) {
this.logger = logger;
}

@Override
public void handleAdminEvent(SVNAdminEvent event, double progress) throws SVNException {
super.handleAdminEvent(event, progress);
logger.println(event.toString());
}

@Override
public void handleEvent(SVNEvent event, double progress) throws SVNException {
super.handleEvent(event, progress);
logger.println(event.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="Hostname with optional port" field="host">
<f:textbox default="${descriptor.getDefaultHost()}"/>
</f:entry>


<f:radioBlock name="zapInstallDescription" value="auto" title="${%Automatically build ZAP}"
checked="${instance.getZapInstallType().equals('auto')}">
<f:entry title="${%OWASP ZAP Repository}" field="repositoryURL">
<f:textbox default="${descriptor.getDefaultRepository()}"
value="${instance.getZapInstallDescription().getRepositoryURL()}"/>
</f:entry>
</f:radioBlock>


<f:radioBlock name="zapInstallDescription" value="manual" title="${%Specify ZAP location on filesystem}"
checked="${instance.getZapInstallType().equals('manual')}">
<f:entry title="${%Path to ZAP}" field="path">
<f:textbox default="${descriptor.getDefaultPath()}" value="${instance.getZapInstallDescription().getPath()}" />
</f:entry>
</f:radioBlock>

</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
</j:jelly>
Loading

0 comments on commit f165367

Please sign in to comment.