├── 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 extends AbstractProject> 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 |
--------------------------------------------------------------------------------