├── src ├── main │ ├── webapp │ │ └── .keep │ ├── resources │ │ ├── org │ │ │ └── jenkinsci │ │ │ │ └── plugins │ │ │ │ └── burpscan │ │ │ │ ├── Messages.properties │ │ │ │ └── BurpScanRecorder │ │ │ │ ├── config.properties │ │ │ │ └── config.jelly │ │ └── index.jelly │ └── java │ │ └── org │ │ └── jenkinsci │ │ └── plugins │ │ └── burpscan │ │ └── BurpScanRecorder.java └── test │ └── java │ └── org │ └── jenkinsci │ └── plugins │ └── burpscan │ └── BurpScanRecorderTest.java ├── .gitignore ├── README.md └── pom.xml /src/main/webapp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | work/ 4 | *.iml 5 | .DS_Store 6 | **/.DS_Store 7 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/burpscan/Messages.properties: -------------------------------------------------------------------------------- 1 | BurpScanRecorder.description=Burp Scan 2 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 |
3 | Use Burp to scan a website and fail the build depending on issue severity/confidence. 4 |
5 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/burpscan/BurpScanRecorderTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.burpscan; 2 | 3 | import org.junit.Test; 4 | 5 | public class BurpScanRecorderTest 6 | { 7 | @Test 8 | public void name() 9 | { 10 | // TODO: test 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Burp Jenkins Scan Plugin 2 | 3 | Jenkins plugin to scan websites using Burp and fail builds if issues are found. 4 | 5 | * Easy configuration to use Burp API to scan fixed or ephemeral websites as part of a Jenkins project 6 | * Configurable thresholds to filter issues to pass/fail builds 7 | 8 | ### Building 9 | ```bash 10 | $ mvn clean package 11 | ``` 12 | Then the plugin file will be in `target/burp-scan.hpi`. -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/burpscan/BurpScanRecorder/config.properties: -------------------------------------------------------------------------------- 1 | ApiUrl=URL of Burp API 2 | ScanDefinitionJson=Scan definition in JSON format 3 | ScanDefinitionJsonDescription=Optional, in the format generated by the web service running on the Burp API endpoint 4 | SeverityThreshold=Lowest severity issue required to fail the build 5 | ConfidenceThreshold=Lowest confidence issue required to fail the build 6 | Timeout=Length of time in seconds to wait before giving up 7 | OutputJsonIssues=Output issues in JSON form (verbose) 8 | SelfSignedCertX509=Self-signed TLS certificate (public part) 9 | SelfSignedCertX509Description=Optional, in X509 base64-encoded format (often a .pem file) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.jenkins-ci.plugins 5 | plugin 6 | 3.8 7 | 8 | 9 | net.portswigger.burp.jenkins.plugins 10 | burp-jenkins-integration 11 | 1.0.7beta 12 | hpi 13 | Burp Scan 14 | Fail the build if Burp finds issues in a website 15 | 16 | 17 | 18 | 19 | repo.jenkins-ci.org 20 | https://repo.jenkins-ci.org/public/ 21 | 22 | 23 | 24 | 25 | 26 | repo.jenkins-ci.org 27 | https://repo.jenkins-ci.org/public/ 28 | 29 | 30 | 31 | 32 | 33 | net.portswigger.burp.api.driver 34 | burp-ci-driver 35 | 1.0.7beta 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 3.7.0 45 | 46 | 1.8 47 | 1.8 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | support 56 | PortSwigger Support 57 | support@portswigger.net 58 | 59 | 60 | 61 | 62 | 2.7.3 63 | 8 64 | false 65 | 1.14.2 66 | true 67 | 2.0.8 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/burpscan/BurpScanRecorder/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Note that the dynamic input for this plugin is the content of the previous step's build output, specifically lines of the form "BURP_SCAN_URL = <URL to scan>" 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/burpscan/BurpScanRecorder.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.burpscan; 2 | 3 | import com.google.common.base.Strings; 4 | import hudson.Extension; 5 | import hudson.Launcher; 6 | import hudson.model.AbstractBuild; 7 | import hudson.model.AbstractProject; 8 | import hudson.model.BuildListener; 9 | import hudson.tasks.BuildStepDescriptor; 10 | import hudson.tasks.BuildStepMonitor; 11 | import hudson.tasks.Builder; 12 | import net.portswigger.burp.api.driver.BurpCiDriver; 13 | import net.portswigger.burp.api.driver.BurpCiSourceConsumer; 14 | import org.kohsuke.stapler.DataBoundConstructor; 15 | 16 | import java.io.BufferedReader; 17 | import java.io.FileReader; 18 | import java.io.IOException; 19 | import java.io.UncheckedIOException; 20 | 21 | public class BurpScanRecorder extends Builder 22 | { 23 | @Extension 24 | public static final BuildStepDescriptor DESCRIPTOR = new BuildStepDescriptor() 25 | { 26 | @Override 27 | public boolean isApplicable(Class jobType) 28 | { 29 | return true; 30 | } 31 | 32 | @Override 33 | public String getDisplayName() { 34 | return "Burp scan"; 35 | } 36 | }; 37 | 38 | private final String apiUrl; 39 | private final String scanDefinitionJson; 40 | private final String severityThreshold; 41 | private final String confidenceThreshold; 42 | private final String timeout; 43 | private boolean outputJsonIssues; 44 | private final String selfSignedCertX509; 45 | 46 | @DataBoundConstructor 47 | public BurpScanRecorder( 48 | String apiUrl, 49 | String scanDefinitionJson, 50 | String severityThreshold, 51 | String confidenceThreshold, 52 | String timeout, 53 | boolean outputJsonIssues, 54 | String selfSignedCertX509 55 | ) { 56 | this.apiUrl = apiUrl; 57 | this.scanDefinitionJson = scanDefinitionJson; 58 | this.severityThreshold = severityThreshold; 59 | this.confidenceThreshold = confidenceThreshold; 60 | this.timeout = timeout; 61 | this.outputJsonIssues = outputJsonIssues; 62 | this.selfSignedCertX509 = selfSignedCertX509; 63 | } 64 | 65 | // getters for data-binding 66 | 67 | @SuppressWarnings("unused") 68 | public String getApiUrl() 69 | { 70 | return apiUrl; 71 | } 72 | 73 | @SuppressWarnings("unused") 74 | public String getScanDefinitionJson() 75 | { 76 | return scanDefinitionJson; 77 | } 78 | 79 | @SuppressWarnings("unused") 80 | public String getSeverityThreshold() 81 | { 82 | return severityThreshold; 83 | } 84 | 85 | @SuppressWarnings("unused") 86 | public String getConfidenceThreshold() 87 | { 88 | return confidenceThreshold; 89 | } 90 | 91 | @SuppressWarnings("unused") 92 | public String getTimeout() 93 | { 94 | return timeout; 95 | } 96 | 97 | @SuppressWarnings("unused") 98 | public boolean getOutputJsonIssues() 99 | { 100 | return outputJsonIssues; 101 | } 102 | 103 | @SuppressWarnings("unused") 104 | public String getSelfSignedCertX509() 105 | { 106 | return selfSignedCertX509; 107 | } 108 | 109 | @Override 110 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 111 | { 112 | // listener.getLogger().println("DEBUG: apiUrl = " + apiUrl); 113 | // listener.getLogger().println("DEBUG: scanDefinitionJson = " + scanDefinitionJson); 114 | // listener.getLogger().println("DEBUG: severityThreshold = " + severityThreshold); 115 | // listener.getLogger().println("DEBUG: confidenceThreshold = " + confidenceThreshold); 116 | // listener.getLogger().println("DEBUG: timeout = " + timeout); 117 | // listener.getLogger().println("DEBUG: outputJsonIssues = " + outputJsonIssues); 118 | 119 | BurpCiSourceConsumer burpCiSourceConsumer = BurpCiSourceConsumer.fromReader(logReader(build)); 120 | 121 | try { 122 | return new BurpCiDriver( 123 | apiUrl, 124 | scanDefinitionJson, 125 | burpCiSourceConsumer.getUrls(), 126 | severityThreshold, 127 | confidenceThreshold, 128 | timeout, 129 | burpCiSourceConsumer.getIgnores(), 130 | null, 131 | null, 132 | outputJsonIssues ? listener.getLogger()::println : null, 133 | selfSignedCertX509 == null || selfSignedCertX509.isEmpty() ? null : selfSignedCertX509) // TODO: in 1.0.7 nullOrEmpty is done in the driver 134 | .scan(listener.getLogger()::println) 135 | .success; 136 | } 137 | catch (IOException e) 138 | { 139 | throw new UncheckedIOException(e); 140 | } 141 | catch (InterruptedException e) 142 | { 143 | throw new RuntimeException(e); 144 | } 145 | } 146 | 147 | private static BufferedReader logReader(AbstractBuild build) 148 | { 149 | try 150 | { 151 | return new BufferedReader(new FileReader(build.getLogFile())); 152 | } 153 | catch (IOException e) 154 | { 155 | throw new UncheckedIOException(e); 156 | } 157 | } 158 | 159 | @Override 160 | public BuildStepMonitor getRequiredMonitorService() 161 | { 162 | return BuildStepMonitor.NONE; 163 | } 164 | 165 | @Override 166 | public BuildStepDescriptor getDescriptor() 167 | { 168 | return DESCRIPTOR; 169 | } 170 | } 171 | --------------------------------------------------------------------------------