├── src
└── main
│ ├── webapp
│ ├── help-phost.html
│ ├── help-useproxy.html
│ ├── help-pport.html
│ ├── help-ncwebsiteid.html
│ ├── help-ncprofileid.html
│ ├── help-acxserverurl.html
│ ├── help-puser.html
│ ├── help-ppassword.html
│ ├── help-ncapitoken.html
│ ├── help-ncconfirmed.html
│ ├── help-ncignoreriskaccepted.html
│ ├── help-ncignorefalsepositive.html
│ ├── help-ncabortscan.html
│ ├── help-ncseverity.html
│ ├── help-ncstopscan.html
│ ├── help-credentialsid.html
│ ├── help-ncscanType.html
│ └── scripts
│ │ └── arrive.js
│ ├── resources
│ ├── index.jelly
│ └── com
│ │ └── acunetix
│ │ └── plugin
│ │ ├── Messages.properties
│ │ ├── ACXScanBuilder
│ │ ├── config.properties
│ │ ├── global.jelly
│ │ └── config.jelly
│ │ └── ACXScanResultAction
│ │ └── index.jelly
│ └── java
│ └── com
│ └── acunetix
│ ├── model
│ ├── ScanType.java
│ ├── WebsiteProfileModel.java
│ ├── ScanTaskState.java
│ ├── ReportType.java
│ ├── WebsiteModel.java
│ ├── ScanCancelRequest.java
│ ├── ScanCancelRequestResult.java
│ ├── ProxyBlock.java
│ ├── ScanRequestBase.java
│ ├── ScanInfoRequest.java
│ ├── WebsiteModelRequest.java
│ ├── ScanRequest.java
│ ├── ScanInfoRequestResult.java
│ ├── VCSCommit.java
│ ├── ScanReport.java
│ ├── IgnoredVulnerabilityStateFilters.java
│ └── ScanRequestResult.java
│ ├── plugin
│ ├── ACXScanSCMAction.java
│ ├── ACXScanSCMListener.java
│ ├── ACXScanResultAction.java
│ └── ACXScanBuilder.java
│ └── utility
│ └── AppCommon.java
├── ss
├── jenkins_scan_report.png
├── jenkins_scan_settings.png
├── jenkins_global_settings.png
└── NE_jenkins_new_integration.png
├── spotbugs-exclude.xml
├── Jenkinsfile
├── .vscode
├── settings.json
├── launch.json
└── tasks.json
├── LICENSE
├── .gitignore
├── Readme.md
├── pom.xml
└── acunetix-360-scan.iml
/src/main/webapp/help-phost.html:
--------------------------------------------------------------------------------
1 |
2 | Enter the proxy host.
3 |
--------------------------------------------------------------------------------
/src/main/webapp/help-useproxy.html:
--------------------------------------------------------------------------------
1 |
2 | Selecting this checkbox enables Proxy.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/webapp/help-pport.html:
--------------------------------------------------------------------------------
1 |
2 | Enter the port number if the proxy host has a port.
3 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncwebsiteid.html:
--------------------------------------------------------------------------------
1 |
2 | This address will be scanned.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncprofileid.html:
--------------------------------------------------------------------------------
1 |
2 | This profile will be used in the scan.
3 |
4 |
--------------------------------------------------------------------------------
/ss/jenkins_scan_report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/acunetix-360-scan-plugin/master/ss/jenkins_scan_report.png
--------------------------------------------------------------------------------
/ss/jenkins_scan_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/acunetix-360-scan-plugin/master/ss/jenkins_scan_settings.png
--------------------------------------------------------------------------------
/ss/jenkins_global_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/acunetix-360-scan-plugin/master/ss/jenkins_global_settings.png
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Plugin for starting Acunetix 360 Scans.
4 |
5 |
--------------------------------------------------------------------------------
/ss/NE_jenkins_new_integration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/acunetix-360-scan-plugin/master/ss/NE_jenkins_new_integration.png
--------------------------------------------------------------------------------
/src/main/webapp/help-acxserverurl.html:
--------------------------------------------------------------------------------
1 |
2 | like https://online.acunetix360.com.
3 |
4 |
--------------------------------------------------------------------------------
/spotbugs-exclude.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | buildPlugin(configurations:[
2 | [platform:"windows", jdk:"21", jenkins:"2.516.3"],
3 | [platform:"linux", jdk:"21", jenkins:"2.516.3"]
4 | ])
--------------------------------------------------------------------------------
/src/main/webapp/help-puser.html:
--------------------------------------------------------------------------------
1 |
2 | If the proxy server is password protected, enter your username and password in the Username and Password fields.
3 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ppassword.html:
--------------------------------------------------------------------------------
1 |
2 | If the proxy server is password protected, enter your username and password in the Username and Password fields.
3 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanType.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | public enum ScanType {
4 | Incremental, FullWithPrimaryProfile, FullWithSelectedProfile
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncapitoken.html:
--------------------------------------------------------------------------------
1 |
2 | It can be found at "Your Account > API Settings" page in the Acunetix 360. User must have "Start Scans" permission for the target website.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncconfirmed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Is Confirmed: Option to filter confirmed scan severities.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "automatic",
3 | "files.exclude": {
4 | "**/.classpath": true,
5 | "**/.project": true,
6 | "**/.settings": true,
7 | "**/.factorypath": true
8 | }
9 | }
--------------------------------------------------------------------------------
/src/main/webapp/help-ncignoreriskaccepted.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Do Not Fail The Build When Scan Contains Accepted Risk: If selected, the build does not fail when the scan contains accepted risk(s).
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncignorefalsepositive.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Do Not Fail The Build When Scan Contains False Positive: If selected, the build does not fail when the scan contains false positive(s).
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncabortscan.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Stop The Scan When Build is Aborted: If this configuration is selected, the scan started by the plugin will be canceled when the Jenkins build is Aborted by the user.
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncseverity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Fail the build if scan contains: Select severity level(s) that will fail the Jenkins build. If you do not want to fail the Jenkins build, select "Do not fail the build".
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncstopscan.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Stop The Scan When Build Fails: If this configuration is selected, the scan started by the plugin will be canceled when the Jenkins build failed.
5 | If a vulnerability matches, the scan will be canceled; and Acunetix will delete the canceled scan.
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/main/webapp/help-credentialsid.html:
--------------------------------------------------------------------------------
1 |
2 | Acunetix 360 Server URL - API Token Pair. Note that setting this credentials overrides jenkins' global settings.
3 | Credential
4 | Kind = Username with password
5 | Scope = Global *(This must be defined as global)
6 | Username = Server Url like https://online.acunetix360.com
7 | Password = Api Token
8 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/WebsiteProfileModel.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | public class WebsiteProfileModel {
4 | private String id;
5 | private String Name;
6 |
7 | public String getId() {
8 | return id;
9 | }
10 |
11 | public void setId(String id) {
12 | this.id = id;
13 | }
14 |
15 | public String getName() {
16 | return Name;
17 | }
18 |
19 | public void setName(String name) {
20 | Name = name;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "java",
9 | "name": "Debug (Attach)",
10 | "request": "attach",
11 | "hostName": "localhost",
12 | "port": 8000,
13 | "preLaunchTask": "mvnDebug"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanTaskState.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | public enum ScanTaskState {
4 | Queued(0), Scanning(1), Archiving(2), Complete(3), Failed(4), Cancelled(5), Delayed(
5 | 6), Pausing(7), Paused(8), Resuming(9);
6 |
7 | private final int number;
8 |
9 | ScanTaskState(final int number) {
10 | this.number = number;
11 | }
12 |
13 | public int getNumber() {
14 | return number;
15 | }
16 |
17 | public String getNumberAsString() {
18 | return String.valueOf(number);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ReportType.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | public enum ReportType {
4 | ScanDetail(3),
5 | OwaspTopTen2013(5),
6 | HIPAACompliance(6),
7 | PCICompliance(7),
8 | KnowledgeBase(8),
9 | ExecutiveSummary(9),
10 | OwaspTopTen2017(10),
11 | SansTop25(11),
12 | WASC(12),
13 | Iso27001Compliance(13),
14 | FullScanDetail(14);
15 |
16 | private final int number;
17 |
18 | ReportType(final int number) {
19 | this.number = number;
20 | }
21 |
22 | public int getNumber() {
23 | return number;
24 | }
25 |
26 | public String getNumberAsString() {
27 | return String.valueOf(number);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/webapp/help-ncscanType.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | Incremental scan: The website's profile is used for retrieving the scan settings. Last scan with the same scan setting will be used as base for the incremental scan.
5 |
6 | -
7 | Full (With primary profile): Performs full scan with primary profile. If no primary profile have been defined yet, default Acunetix 360 scan settings will be used.
8 |
9 | -
10 | Full (With selected profile): Performs full scan with provided profile settings.
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/resources/com/acunetix/plugin/Messages.properties:
--------------------------------------------------------------------------------
1 | ACXScanBuilder.DescriptorImpl.errors.missingApiURL=The parameter 'Acunetix 360 Server URL' must be specified.
2 | ACXScanBuilder.DescriptorImpl.errors.invalidApiURL=The parameter 'Acunetix 360 Server URL' is invalid.
3 | ACXScanBuilder.DescriptorImpl.errors.missingApiToken=The parameter 'API Token' must be specified.
4 | ACXScanBuilder.DescriptorImpl.errors.invalidScanType=The parameter 'Scan Type' is invalid.
5 | ACXScanBuilder.DescriptorImpl.errors.invalidWebsiteId=The parameter 'Website URL' is invalid.
6 | ACXScanBuilder.DescriptorImpl.errors.invalidProfileId=The parameter 'Profile Name' is invalid.
7 | ACXScanBuilder.DescriptorImpl.DisplayName=Acunetix 360 Scan
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/plugin/ACXScanSCMAction.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.plugin;
2 |
3 | import com.acunetix.model.VCSCommit;
4 | import hudson.model.Action;
5 |
6 | import edu.umd.cs.findbugs.annotations.CheckForNull;
7 |
8 | public class ACXScanSCMAction implements Action {
9 |
10 | private final VCSCommit vcsCommit;
11 |
12 | public ACXScanSCMAction(VCSCommit vcsCommit) {
13 | this.vcsCommit = vcsCommit;
14 | }
15 |
16 | public VCSCommit getVcsCommit() {
17 | return vcsCommit;
18 | }
19 |
20 | @CheckForNull
21 | @Override
22 | public String getIconFileName() {
23 | return null;
24 | }
25 |
26 | @CheckForNull
27 | @Override
28 | public String getDisplayName() {
29 | return "ACXScanSCMAction";
30 | }
31 |
32 | @CheckForNull
33 | @Override
34 | public String getUrlName() {
35 | return null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/resources/com/acunetix/plugin/ACXScanBuilder/config.properties:
--------------------------------------------------------------------------------
1 | acxServerURL=NC Server URL
2 | acxServerURLDescr=URL, like https://online.acunetix360.com.
3 | apiToken=API Token
4 | apiTokenDescr=It can be found at "API Settings" page in the Acunetix 360 which is reachable under "Your Accounts" section in Acunetix 360 Site. Token's user must have "start scan permission" on to be scanned website.
5 | ncScanType=Scan Type
6 | scanTypeDescr=
7 | ncReportType=Report Type
8 | reportTypeDescr=
9 | ncTargetURL=Website Deploy URL
10 | targetURLDescr=Website deploy URL, like http://www.example.com. This address will be scanned.
11 | ncProfileName=Profile Name
12 | profileNameDescr=
13 | ncBaseScanId=Base Scan Id:
14 | baseScanIdDescr=Base scan ID like, 8539ae41-a816-4d43-b91e-3b4296002d72. Base scan ID is a scan ID which can be retrieved from "/scans/list" endpoint of Acunetix 360 API.
15 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/WebsiteModel.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import java.util.ArrayList;
4 |
5 | public class WebsiteModel {
6 | private String id;
7 | private String name;
8 | private String url;
9 | private ArrayList profiles;
10 |
11 | public WebsiteModel() {
12 | profiles = new ArrayList<>();
13 | }
14 |
15 | public String getId() {
16 | return id;
17 | }
18 |
19 | public void setId(String id) {
20 | this.id = id;
21 | }
22 |
23 | public String getName() {
24 | return name;
25 | }
26 |
27 | public void setName(String name) {
28 | this.name = name;
29 | }
30 |
31 | public String getUrl() {
32 | return url;
33 | }
34 |
35 | public void setUrl(String url) {
36 | this.url = url;
37 | }
38 |
39 | public String getDisplayName() {
40 | return getName() + " (" + getUrl() + ")";
41 | }
42 |
43 | public ArrayList getProfiles() {
44 | return profiles;
45 | }
46 |
47 | public void setProfiles(ArrayList profiles) {
48 | this.profiles = profiles;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Acunetix Ltd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/plugin/ACXScanSCMListener.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.plugin;
2 |
3 | import com.acunetix.model.VCSCommit;
4 | import hudson.Extension;
5 | import hudson.model.*;
6 | import hudson.model.listeners.SCMListener;
7 | import hudson.scm.ChangeLogSet;
8 | import hudson.scm.SCM;
9 |
10 | /**
11 | * This class registers an {@link SCMListener} with Jenkins which allows us to create the "Checkout
12 | * successful" event.
13 | */
14 | @Extension
15 | public class ACXScanSCMListener extends SCMListener {
16 | /**
17 | * Invoked right after the source code for the build has been checked out. It will NOT be called
18 | * if a checkout fails.
19 | *
20 | * @param build - Current build
21 | * @param scm - Configured SCM
22 | * @param listener - Current build listener
23 | * @param changelog - Changelog
24 | * @throws Exception if an error is encountered
25 | */
26 | @Override
27 | public void onChangeLogParsed(Run, ?> build, SCM scm, TaskListener listener,
28 | ChangeLogSet> changelog) throws Exception {
29 | super.onChangeLogParsed(build, scm, listener, changelog);
30 |
31 | build.replaceAction(new ACXScanSCMAction(new VCSCommit(build, changelog)));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "mvnClean",
8 | "type": "shell",
9 | "command": "mvn clean",
10 | "isBackground": true,
11 | "problemMatcher": [
12 | {
13 | "pattern": [
14 | {
15 | "regexp": "\\b\\B",
16 | "file": 1,
17 | "location": 2,
18 | "message": 3
19 | }
20 | ],
21 | "background": {
22 | "activeOnStart": true,
23 | "beginsPattern": "^.*Preparing to execute Maven clean.*",
24 | "endsPattern": "^.*End of maven clean*"
25 | }
26 | }
27 | ]
28 | },
29 | {
30 | "label": "mvnDebug",
31 | "type": "shell",
32 | "dependsOn": ["mvnClean"],
33 | "command": "mvnDebug hpi:run -D jenkins.version=2.516.3",
34 | "isBackground": true,
35 | "problemMatcher": [
36 | {
37 | "pattern": [
38 | {
39 | "regexp": "\\b\\B",
40 | "file": 1,
41 | "location": 2,
42 | "message": 3
43 | }
44 | ],
45 | "background": {
46 | "activeOnStart": true,
47 | "beginsPattern": "^.*Preparing to execute Maven in debug mode.*",
48 | "endsPattern": "^.*Listening for transport dt_socket at address.*"
49 | }
50 | }
51 | ]
52 | }
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Java template
3 | # Compiled class file
4 | *.class
5 |
6 | # Log file
7 | *.log
8 |
9 | # Ignore bin directory (generated build output, not needed in version control)
10 | bin/
11 |
12 | # BlueJ files
13 | *.ctxt
14 |
15 | # Mobile Tools for Java (J2ME)
16 | .mtj.tmp/
17 |
18 | # Package Files #
19 | *.jar
20 | *.war
21 | *.ear
22 | *.zip
23 | *.tar.gz
24 | *.rar
25 |
26 | work/
27 | target/
28 | pom.xml.tag
29 | pom.xml.releaseBackup
30 | pom.xml.versionsBackup
31 | pom.xml.next
32 | release.properties
33 | dependency-reduced-pom.xml
34 | buildNumber.properties
35 | .mvn/timing.properties
36 |
37 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
38 | !/.mvn/wrapper/maven-wrapper.jar
39 |
40 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
41 | hs_err_pid*
42 | ### Windows template
43 | # Windows thumbnail cache files
44 | Thumbs.db
45 | ehthumbs.db
46 | ehthumbs_vista.db
47 |
48 | # Dump file
49 | *.stackdump
50 |
51 | # Folder config file
52 | [Dd]esktop.ini
53 |
54 | # Recycle Bin used on file shares
55 | $RECYCLE.BIN/
56 |
57 | # Windows Installer files
58 | *.cab
59 | *.msi
60 | *.msm
61 | *.msp
62 |
63 | # Windows shortcuts
64 | *.lnk
65 |
66 | # CMake
67 | cmake-build-debug/
68 |
69 | ## File-based project format:
70 | *.iws
71 |
72 | ## Plugin-specific files:
73 |
74 | # IntelliJ
75 | out/
76 | .idea/
77 |
78 | # JIRA plugin
79 | atlassian-ide-plugin.xml
80 |
81 | # Crashlytics plugin (for Android Studio and IntelliJ)
82 | com_crashlytics_export_strings.xml
83 | crashlytics.properties
84 | crashlytics-build.properties
85 | fabric.properties
86 |
87 | *.classpath
88 | *.factorypath
89 | *.project
90 | *.prefs
--------------------------------------------------------------------------------
/src/main/resources/com/acunetix/plugin/ACXScanResultAction/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanCancelRequest.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import hudson.util.Secret;
4 | import org.apache.hc.core5.http.HttpHeaders;
5 | import org.apache.hc.core5.http.io.entity.StringEntity;
6 | import org.apache.hc.core5.http.ClassicHttpResponse;
7 | import org.apache.hc.client5.http.classic.methods.HttpPost;
8 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
9 |
10 | import java.io.IOException;
11 | import java.net.MalformedURLException;
12 | import java.net.URI;
13 | import java.net.URISyntaxException;
14 | import java.net.URL;
15 | import org.apache.hc.core5.http.HttpEntity;
16 | import org.apache.hc.core5.http.ContentType;
17 |
18 | public class ScanCancelRequest extends ScanRequestBase {
19 | public ScanCancelRequest(String apiURL, Secret apiToken, String scanTaskId,
20 | ProxyBlock proxy) throws MalformedURLException, NullPointerException, URISyntaxException {
21 | super(apiURL, apiToken, proxy);
22 | this.scanTaskId = scanTaskId;
23 | this.scanCancelUri = new URL(ApiURL, "api/1.0/scans/CancelScanForPlugin/").toURI();
24 | this.contentLength = "0";
25 | this.content = "";
26 | }
27 |
28 | public final String scanTaskId;
29 | public final URI scanCancelUri;
30 | public final String contentLength;
31 | public final String content;
32 |
33 | public ClassicHttpResponse scanCancelRequest() throws IOException {
34 | CloseableHttpClient client = getHttpClient();
35 | HttpPost httpPost = new HttpPost(scanCancelUri + scanTaskId);
36 | HttpEntity stringEntity = new StringEntity(content,ContentType.APPLICATION_JSON);
37 | httpPost.setEntity(stringEntity);
38 | httpPost.setHeader("Accept", json);
39 | httpPost.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
40 |
41 | return (ClassicHttpResponse) client.execute(httpPost);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/resources/com/acunetix/plugin/ACXScanBuilder/global.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
39 |
40 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | Acunetix 360 Scan Plugin
2 | ====================
3 |
4 | [](https://plugins.jenkins.io/acunetix-360-scan)
5 | [](https://plugins.jenkins.io/acunetix-360-scan)
6 |
7 | ## About this plugin
8 |
9 | Allows users to start security scans via Acunetix 360 and see their
10 | reports in Jenkins
11 |
12 | ## Features
13 |
14 | ### Global Settings
15 |
16 | Acunetix 360 plugin needs the admin user to define the API settings
17 | only once.
18 |
19 | 
20 |
21 | ### Global Settings Override
22 |
23 | Global settings can be overridden in pipeline scripts by
24 | giving ncApiToken and/or acxServerURL parameters.
25 |
26 | #### Example Script
27 |
28 | step([$class: 'ACXScanBuilder', ncScanType: 'FullWithPrimaryProfile', ncWebsiteId: '19011b1b-4141-4331-8514-ab4102a4c135'])
29 |
30 | 
31 |
32 | ### Scan Settings
33 |
34 | Once you define global API settings, the plugin retrieves available
35 | scan settings such as scannable website list and scan profile names. You
36 | can easily select relevant settings.
37 |
38 | 
39 |
40 | ### Scan Report
41 |
42 | Once your initiated scan is completed, you can easily see your
43 | executive scan report on the build result window.
44 |
45 | 
46 |
47 | ## Requirements
48 |
49 | In order to use the Acunetix 360 scan plugin, following requirements
50 | needs to be satisfied:
51 |
52 | - The user must have API token which has permission to start security
53 | scan.
54 |
55 | - The token belongs to the Acunetix 360 account must have at least one
56 | registered website.
57 |
58 | ## User Guide
59 |
60 | Acunetix 360 Jenkins Plugin documentation is available at:
61 |
62 |
63 |
64 | Acunetix 360 SDLC documentation is available at:
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/plugin/ACXScanResultAction.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.plugin;
2 |
3 | import com.acunetix.model.ScanRequestResult;
4 | import hudson.model.Action;
5 | import hudson.model.Run;
6 | import hudson.util.HttpResponses;
7 | import jenkins.model.RunAction2;
8 | import org.kohsuke.stapler.HttpResponse;
9 | import org.kohsuke.stapler.verb.GET;
10 |
11 | import edu.umd.cs.findbugs.annotations.CheckForNull;
12 |
13 | public class ACXScanResultAction implements Action, RunAction2 {
14 |
15 | private ScanRequestResult scanRequestResult;
16 |
17 | private transient Run run;
18 |
19 | public ACXScanResultAction(ScanRequestResult scanRequestResult) {
20 | this.scanRequestResult = scanRequestResult;
21 | }
22 |
23 | public ScanRequestResult getScanRequestResult() {
24 | return scanRequestResult;
25 | }
26 |
27 | public void setScanRequestResult(ScanRequestResult scanRequestResult) {
28 | this.scanRequestResult = scanRequestResult;
29 | }
30 |
31 | public String getError() {
32 | if (scanRequestResult.isError()) {
33 | return "true";
34 | } else {
35 | return "false";
36 | }
37 | }
38 |
39 | public String getReportGenerated() {
40 | if (scanRequestResult.isReportGenerated()) {
41 | return "true";
42 | } else {
43 | return "false";
44 | }
45 | }
46 |
47 | @GET
48 | public HttpResponse doGetContent() {
49 | String content = scanRequestResult.getReport().getContent();
50 | return HttpResponses.html(content);
51 | }
52 |
53 | @CheckForNull
54 | @Override
55 | public String getIconFileName() {
56 | return "document.png";
57 | }
58 |
59 | @CheckForNull
60 | @Override
61 | public String getDisplayName() {
62 | return "Acunetix 360 Report";
63 | }
64 |
65 | @CheckForNull
66 | @Override
67 | public String getUrlName() {
68 | return "acunetixreport";
69 | }
70 |
71 | @Override
72 | public void onAttached(Run, ?> run) {
73 | this.run = run;
74 | }
75 |
76 | @Override
77 | public void onLoad(Run, ?> run) {
78 | this.run = run;
79 | }
80 |
81 | public Run getRun() {
82 | return run;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanCancelRequestResult.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import org.apache.hc.core5.http.ClassicHttpResponse;
4 | import org.json.simple.parser.ParseException;
5 |
6 | import java.io.IOException;
7 | import java.net.MalformedURLException;
8 | import java.net.URISyntaxException;
9 |
10 | import com.acunetix.utility.AppCommon;
11 |
12 | public class ScanCancelRequestResult extends ScanRequestBase {
13 | public static ScanCancelRequestResult errorResult(final String errorMessage) {
14 | return new ScanCancelRequestResult(errorMessage);
15 | }
16 |
17 | private final int httpStatusCode;
18 | private String data;
19 | private String scanTaskID;
20 | private boolean isError;
21 | private String errorMessage;
22 |
23 | private ScanCancelRequestResult(final String errorMessage) {
24 | super();
25 | this.errorMessage = errorMessage;
26 | httpStatusCode = 0;
27 | isError = true;
28 | data = "";
29 | }
30 |
31 | public ScanCancelRequestResult(final ClassicHttpResponse response) throws MalformedURLException, URISyntaxException {
32 | super();
33 | httpStatusCode = response.getCode();
34 | isError = httpStatusCode != 200;
35 |
36 | if (!isError) {
37 | try {
38 | data = AppCommon.parseResponseToString(response);
39 | isError = !(boolean) AppCommon.parseJsonValue(data, "IsValid");
40 | if (!isError) {
41 | scanTaskID = (String) AppCommon.parseJsonValue(data, "ScanTaskId");
42 | } else {
43 | errorMessage = (String) AppCommon.parseJsonValue(data, "ErrorMessage");
44 | }
45 | } catch (final ParseException ex) {
46 | isError = true;
47 | errorMessage = "Scan info request result is not parsable::: " + ex.toString();
48 | } catch (final IOException ex) {
49 | isError = true;
50 | errorMessage = "Scan info request result is not readable::: " + ex.toString();
51 | }
52 | }
53 | }
54 |
55 | public String getScanTaskId() {
56 | return scanTaskID;
57 | }
58 |
59 | public int getHttpStatusCode() {
60 | return httpStatusCode;
61 | }
62 |
63 | public String getErrorMessage() {
64 | return errorMessage;
65 | }
66 |
67 | public boolean isError() {
68 | return isError;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ProxyBlock.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import org.kohsuke.stapler.DataBoundConstructor;
4 |
5 | /**
6 | * The ProxyBlock class corresponds to an "optionalBlock" global.jelly element that
7 | * represents proxy configuration data.
8 | *
9 | */
10 | public class ProxyBlock {
11 |
12 | private final Boolean _useProxy;
13 | private final String _pHost;
14 | private final String _pPort;
15 | private final String _pUser;
16 | private final String _pPassword;
17 |
18 | /**
19 | * Corresponds to the {@code useProxy} identifier referenced in a global.jelly file.
20 | *
21 | * @return a {@link java.lang.Boolean} object.
22 | */
23 | public Boolean getUseProxy() {
24 | return this._useProxy;
25 | }
26 |
27 | /**
28 | * Corresponds to the {@code pHost} identifier referenced in a global.jelly file.
29 | *
30 | * @return a {@link java.lang.String} object.
31 | */
32 | public String getpHost() {
33 | return this._pHost;
34 | }
35 |
36 | /**
37 | * Corresponds to the {@code pPort} identifier referenced in a global.jelly file.
38 | *
39 | * @return a {@link java.lang.String} object.
40 | */
41 | public String getpPort() {
42 | return this._pPort;
43 | }
44 |
45 | /**
46 | * Corresponds to the {@code pUser} identifier referenced in a global.jelly file.
47 | *
48 | * @return a {@link java.lang.String} object.
49 | */
50 | public String getpUser() {
51 | return this._pUser;
52 | }
53 |
54 | /**
55 | * Corresponds to the {@code pPassword} identifier referenced in a global.jelly file.
56 | *
57 | * @return a {@link java.lang.String} object.
58 | */
59 | public String getpPassword() {
60 | return this._pPassword;
61 | }
62 |
63 |
64 | /**
65 | * Called by Jenkins with form data.
66 | *
67 | * @param useProxy a {@link java.lang.Boolean} object.
68 | * @param pHost a {@link java.lang.String} object.
69 | * @param pPort a {@link java.lang.String} object.
70 | * @param pUser a {@link java.lang.String} object.
71 | * @param pPassword a {@link java.lang.String} object.
72 | */
73 | @DataBoundConstructor
74 | public ProxyBlock(Boolean useProxy, String pHost, String pPort, String pUser, String pPassword) {
75 | this._useProxy = useProxy;
76 | this._pHost = pHost;
77 | this._pPort = pPort;
78 | this._pUser = pUser;
79 | this._pPassword = pPassword;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanRequestBase.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.acunetix.utility.AppCommon;
4 | import hudson.util.Secret;
5 | import org.apache.commons.codec.binary.Base64;
6 | import org.apache.hc.client5.http.auth.AuthScope;
7 | import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
8 | import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
9 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
10 | import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
11 | import org.apache.hc.core5.http.HttpHost;
12 |
13 | import java.net.MalformedURLException;
14 | import java.net.URL;
15 | import java.nio.charset.StandardCharsets;
16 |
17 | public abstract class ScanRequestBase {
18 | protected static final String json = "application/json";
19 | public final URL ApiURL;
20 | public final Secret ApiToken;
21 | public final ProxyBlock proxy;
22 |
23 | // Called from server-side
24 | public ScanRequestBase(String apiURL, Secret apiToken, ProxyBlock proxy) throws MalformedURLException {
25 | this.ApiURL = AppCommon.getBaseURL(apiURL);
26 | this.ApiToken = apiToken;
27 | this.proxy = proxy;
28 | }
29 |
30 | public ScanRequestBase() {
31 | this.ApiURL = null;
32 | this.ApiToken = null;
33 | this.proxy = null;
34 | }
35 |
36 | protected CloseableHttpClient getHttpClient() {
37 |
38 | if (proxy != null && proxy.getUseProxy()) {
39 | int proxyPort = Integer.parseInt(proxy.getpPort());
40 | HttpHost proxyHttpHost = new HttpHost(proxy.getpHost(), proxyPort);
41 |
42 | BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
43 | AuthScope authScope = new AuthScope(proxyHttpHost);
44 | UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
45 | proxy.getpUser(), proxy.getpPassword().toCharArray());
46 | credsProvider.setCredentials(authScope, credentials);
47 |
48 | return HttpClientBuilder
49 | .create()
50 | .setProxy(proxyHttpHost)
51 | .setDefaultCredentialsProvider(credsProvider)
52 | .disableRedirectHandling()
53 | .build();
54 | }
55 | else {
56 | return HttpClientBuilder
57 | .create()
58 | .disableRedirectHandling()
59 | .build();
60 | }
61 | }
62 |
63 | protected String getAuthHeader() {
64 | String auth = ":" + ApiToken.getPlainText();
65 | byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1));
66 | String authHeader = "Basic " + new String(encodedAuth, StandardCharsets.ISO_8859_1);
67 |
68 | return authHeader;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanInfoRequest.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import hudson.util.Secret;
4 | import org.apache.hc.core5.http.HttpHeaders;
5 | import org.apache.hc.core5.http.ClassicHttpResponse;
6 | import org.apache.hc.client5.http.classic.HttpClient;
7 | import org.apache.hc.client5.http.classic.methods.HttpGet;
8 | import org.apache.hc.client5.http.classic.methods.HttpPost;
9 | import org.apache.hc.core5.http.ContentType;
10 | import org.apache.hc.core5.http.io.entity.StringEntity;
11 | import java.io.IOException;
12 | import java.net.MalformedURLException;
13 | import java.net.URI;
14 | import java.net.URISyntaxException;
15 | import java.net.URL;
16 |
17 | public class ScanInfoRequest extends ScanRequestBase {
18 | public ScanInfoRequest(String apiURL, Secret apiToken,String scanTaskId, Boolean doNotFail, Boolean isConfirmed,
19 | IgnoredVulnerabilityStateFilters filters, ProxyBlock proxy) throws MalformedURLException, NullPointerException, URISyntaxException {
20 | super(apiURL, apiToken, proxy);
21 | this.scanTaskId = scanTaskId;
22 | this.scanInfoUri = new URL(ApiURL, "api/1.0/scans/ScanInfoForPlugin/").toURI();
23 | this.filters = filters;
24 | this.doNotFail = doNotFail;
25 | this.isConfirmed = isConfirmed;
26 | }
27 |
28 | public final String scanTaskId;
29 | public final URI scanInfoUri;
30 | public final IgnoredVulnerabilityStateFilters filters;
31 | public final Boolean doNotFail;
32 | public final Boolean isConfirmed;
33 |
34 | public ClassicHttpResponse scanInfoRequestOld() throws IOException {
35 | HttpClient client = getHttpClient();
36 | final HttpGet httpGet = new HttpGet(scanInfoUri + scanTaskId);
37 | httpGet.setHeader("Accept", json);
38 | httpGet.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
39 |
40 | return (ClassicHttpResponse) client.execute(httpGet);
41 | }
42 |
43 | public ClassicHttpResponse scanInfoRequest() throws IOException {
44 | HttpClient httpClient = getHttpClient();
45 |
46 | try {
47 | HttpPost request = new HttpPost(scanInfoUri);
48 |
49 | StringBuilder jsonString = new StringBuilder();
50 | jsonString.append("{");
51 | jsonString.append("'ScanId':'").append(scanTaskId).append("',");
52 | jsonString.append("'DoNotFail':").append(doNotFail).append(",");
53 | jsonString.append("'IsConfirmed':").append(isConfirmed).append(",");
54 | filters.setFiltersString();
55 | jsonString.append("'IgnoredVulnerabilityStateFilters':").append(filters.getFiltersString());
56 | jsonString.append("}");
57 | StringEntity bodyEntity = new StringEntity(jsonString.toString(), ContentType.APPLICATION_JSON);
58 | // send a JSON data
59 | request.setEntity(bodyEntity);
60 | request.addHeader("Accept","application/json");
61 | request.setHeader("Content-Type", "application/json");
62 | request.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
63 |
64 | return (ClassicHttpResponse) httpClient.execute(request);
65 | }catch (Exception ex) {
66 | Exception e = ex;
67 | }
68 | return null;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/WebsiteModelRequest.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.acunetix.utility.AppCommon;
4 | import hudson.util.Secret;
5 |
6 | import org.apache.hc.core5.http.ClassicHttpResponse;
7 | import org.apache.hc.core5.http.HttpHeaders;
8 | import org.apache.hc.client5.http.classic.HttpClient;
9 | import org.apache.hc.client5.http.classic.methods.HttpGet;
10 | import org.json.simple.JSONArray;
11 | import org.json.simple.JSONObject;
12 | import org.json.simple.parser.JSONParser;
13 | import org.json.simple.parser.ParseException;
14 |
15 | import java.io.IOException;
16 | import java.net.MalformedURLException;
17 | import java.net.URI;
18 | import java.net.URISyntaxException;
19 | import java.net.URL;
20 | import java.util.ArrayList;
21 |
22 | public class WebsiteModelRequest extends ScanRequestBase {
23 | private ArrayList websiteModels = new ArrayList<>();
24 |
25 | public WebsiteModelRequest(String apiURL, Secret apiToken,
26 | ProxyBlock proxy) throws MalformedURLException, NullPointerException, URISyntaxException {
27 | super(apiURL, apiToken, proxy);
28 | pluginWebSiteModelsUri = new URL(ApiURL, "api/1.0/scans/PluginWebSiteModels").toURI();
29 | }
30 |
31 | private final URI pluginWebSiteModelsUri;
32 |
33 | public ArrayList getWebsiteModels() {
34 | return websiteModels;
35 | }
36 |
37 | public ClassicHttpResponse getPluginWebSiteModels() throws IOException, ParseException {
38 | final HttpClient httpClient = getHttpClient();
39 | final HttpGet httpGet = new HttpGet(pluginWebSiteModelsUri);
40 | httpGet.setHeader("Accept", json);
41 | httpGet.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
42 |
43 | ClassicHttpResponse response = (ClassicHttpResponse) httpClient.execute(httpGet);
44 | if (response.getCode() == 200) {
45 | parseWebsiteData(response);
46 | }
47 | return response;
48 | }
49 |
50 | private void parseWebsiteData(final ClassicHttpResponse response) throws ParseException, IOException {
51 | String data = AppCommon.parseResponseToString(response);
52 |
53 | JSONParser parser = new JSONParser();
54 | Object jsonData = parser.parse(data);
55 |
56 | JSONArray WebsiteModelObjects = (JSONArray) jsonData;
57 | websiteModels = new ArrayList<>();
58 |
59 | for (Object wmo : WebsiteModelObjects) {
60 | if (wmo instanceof JSONObject) {
61 | JSONObject websiteModelObject = (JSONObject) wmo;
62 |
63 | WebsiteModel websiteModel = new WebsiteModel();
64 | websiteModel.setId((String) websiteModelObject.get("Id"));
65 | websiteModel.setName((String) websiteModelObject.get("Name"));
66 | websiteModel.setUrl((String) websiteModelObject.get("Url"));
67 |
68 | JSONArray WebsiteProfileModelObjects =
69 | (JSONArray) websiteModelObject.get("WebsiteProfiles");
70 | ArrayList profiles = new ArrayList<>();
71 | for (Object wmpo : WebsiteProfileModelObjects) {
72 | JSONObject websiteProfileModelObject = (JSONObject) wmpo;
73 |
74 | WebsiteProfileModel websiteProfileModel = new WebsiteProfileModel();
75 | websiteProfileModel.setId((String) websiteProfileModelObject.get("Id"));
76 | websiteProfileModel.setName((String) websiteProfileModelObject.get("Name"));
77 |
78 | profiles.add(websiteProfileModel);
79 | }
80 |
81 | websiteModel.setProfiles(profiles);
82 | websiteModels.add(websiteModel);
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanRequest.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import hudson.util.Secret;
4 | import org.apache.hc.core5.http.HttpHeaders;
5 | import org.apache.hc.core5.http.ClassicHttpResponse;
6 | import org.apache.hc.core5.http.NameValuePair;
7 | import org.apache.hc.client5.http.classic.HttpClient;
8 | import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
9 | import org.apache.hc.client5.http.classic.methods.HttpPost;
10 | import org.apache.hc.core5.http.message.BasicNameValuePair;
11 |
12 | import java.io.IOException;
13 | import java.net.MalformedURLException;
14 | import java.net.URI;
15 | import java.net.URISyntaxException;
16 | import java.net.URL;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | public class ScanRequest extends ScanRequestBase {
21 | public ScanRequest(String apiURL, Secret apiToken, String scanType, String websiteId, String profileId, VCSCommit vcsCommit,
22 | ProxyBlock proxy)
23 | throws MalformedURLException, NullPointerException, URISyntaxException {
24 | super(apiURL, apiToken, proxy);
25 | this.scanType = ScanType.valueOf(scanType);
26 | this.websiteId = websiteId;
27 | this.profileId = profileId;
28 | this.vcsCommit = vcsCommit;
29 | scanUri = new URL(ApiURL, "api/1.0/scans/CreateFromPluginScanRequest").toURI();
30 | testUri = new URL(ApiURL, "api/1.0/scans/VerifyPluginScanRequest").toURI();
31 | }
32 |
33 | public final ScanType scanType;
34 | public final String websiteId;
35 | public final String profileId;
36 | public final VCSCommit vcsCommit;
37 | public final URI scanUri;
38 | public final URI testUri;
39 |
40 | public ClassicHttpResponse scanRequest() throws IOException {
41 | HttpClient client = getHttpClient();
42 | final HttpPost httpPost = new HttpPost(scanUri);
43 | httpPost.setHeader("Accept", json);
44 | httpPost.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
45 |
46 | List params = new ArrayList<>();
47 | setScanParams(params);
48 | vcsCommit.addVcsCommitInfo(params);
49 | httpPost.setEntity(new UrlEncodedFormEntity(params));
50 |
51 | return (ClassicHttpResponse) client.execute(httpPost);
52 | }
53 |
54 | public ClassicHttpResponse testRequest() throws IOException {
55 | HttpClient client = getHttpClient();
56 | final HttpPost httpPost = new HttpPost(testUri);
57 | httpPost.setHeader("Accept", json);
58 | httpPost.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
59 |
60 | List params = new ArrayList<>();
61 | setScanParams(params);
62 | httpPost.setEntity(new UrlEncodedFormEntity(params));
63 |
64 | return (ClassicHttpResponse) client.execute(httpPost);
65 | }
66 |
67 | private void setScanParams(List params) {
68 | switch (scanType) {
69 | case Incremental:
70 | params.add(new BasicNameValuePair("WebsiteId", websiteId));
71 | params.add(new BasicNameValuePair("ProfileId", profileId));
72 | params.add(new BasicNameValuePair("ScanType", "Incremental"));
73 | break;
74 | case FullWithPrimaryProfile:
75 | params.add(new BasicNameValuePair("WebsiteId", websiteId));
76 | params.add(new BasicNameValuePair("ScanType", "FullWithPrimaryProfile"));
77 | break;
78 | case FullWithSelectedProfile:
79 | params.add(new BasicNameValuePair("WebsiteId", websiteId));
80 | params.add(new BasicNameValuePair("ProfileId", profileId));
81 | params.add(new BasicNameValuePair("ScanType", "FullWithSelectedProfile"));
82 | break;
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanInfoRequestResult.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.google.gson.Gson;
4 | import com.acunetix.utility.AppCommon;
5 | import org.apache.hc.core5.http.ClassicHttpResponse;
6 | import org.json.simple.parser.ParseException;
7 |
8 | import java.io.IOException;
9 | import java.net.MalformedURLException;
10 | import java.net.URISyntaxException;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | public class ScanInfoRequestResult extends ScanRequestBase {
15 | public static ScanInfoRequestResult errorResult(final String errorMessage) {
16 | return new ScanInfoRequestResult(errorMessage);
17 | }
18 |
19 | private final int httpStatusCode;
20 | private String data;
21 | private String scanTaskID;
22 | private ScanTaskState scanTaskState;
23 | private HashMap FoundedSeverityAndCounts;
24 | private boolean isError;
25 | private String errorMessage;
26 |
27 | private ScanInfoRequestResult(final String errorMessage) {
28 | super();
29 | this.errorMessage = errorMessage;
30 | httpStatusCode = 0;
31 | FoundedSeverityAndCounts = new HashMap();
32 | isError = true;
33 | data = "";
34 | }
35 |
36 | public ScanInfoRequestResult(final ClassicHttpResponse response) throws MalformedURLException, URISyntaxException {
37 | super();
38 | httpStatusCode = response.getCode();
39 | isError = httpStatusCode != 200;
40 |
41 | if (!isError) {
42 | try {
43 | data = AppCommon.parseResponseToString(response);
44 | isError = !(boolean) AppCommon.parseJsonValue(data, "IsValid");
45 | if (!isError) {
46 | scanTaskID = (String) AppCommon.parseJsonValue(data, "ScanTaskId");
47 |
48 | final String sTaskState = (String) AppCommon.parseJsonValue(data, "State");
49 | scanTaskState = ScanTaskState.valueOf(sTaskState);
50 |
51 | org.json.simple.JSONObject foundedSeverityInfo = (org.json.simple.JSONObject) AppCommon
52 | .parseJsonValue(data, "FoundedSeverityAndCounts");
53 |
54 | if (foundedSeverityInfo != null) {
55 | FoundedSeverityAndCounts = new Gson().fromJson(foundedSeverityInfo.toString(), HashMap.class);
56 | }
57 |
58 | if(FoundedSeverityAndCounts == null){
59 | FoundedSeverityAndCounts = new HashMap();
60 | }
61 |
62 | } else {
63 | errorMessage = (String) AppCommon.parseJsonValue(data, "ErrorMessage");
64 | }
65 | } catch (final ParseException ex) {
66 | isError = true;
67 | errorMessage = "Scan info request result is not parsable::: " + ex.toString();
68 | } catch (final IOException ex) {
69 | isError = true;
70 | errorMessage = "Scan info request result is not readable::: " + ex.toString();
71 | }
72 | }
73 | }
74 |
75 | public String getScanTaskId() {
76 | return scanTaskID;
77 | }
78 |
79 | public ScanTaskState getScanTaskState() {
80 | return scanTaskState;
81 | }
82 |
83 | public HashMap getFoundedSeverityAndCounts() {
84 | return FoundedSeverityAndCounts;
85 | }
86 |
87 | public int getHttpStatusCode() {
88 | return httpStatusCode;
89 | }
90 |
91 | public String getErrorMessage() {
92 | return errorMessage;
93 | }
94 |
95 | public boolean isError() {
96 | return isError;
97 | }
98 |
99 | public boolean checkSeverity(final String ncSeverity) {
100 | if (isError()) {
101 | return false;
102 | } else if (ncSeverity == null) {
103 | return false;
104 | } else {
105 | for (Map.Entry entry : this.getFoundedSeverityAndCounts().entrySet()) {
106 | String foundedSeverityLevel = entry.getKey();
107 | if (ncSeverity.contains(foundedSeverityLevel)) {
108 | return true;
109 | }
110 | }
111 |
112 | return false;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/VCSCommit.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.acunetix.utility.AppCommon;
4 | import hudson.model.Run;
5 | import hudson.model.User;
6 | import hudson.scm.ChangeLogSet;
7 | import hudson.util.VersionNumber;
8 | import jenkins.model.Jenkins;
9 | import org.apache.hc.core5.http.NameValuePair;
10 | import org.apache.hc.core5.http.message.BasicNameValuePair;
11 |
12 | import java.text.SimpleDateFormat;
13 | import java.util.Date;
14 | import java.util.List;
15 |
16 | public class VCSCommit {
17 |
18 | private final String ciBuildServerVersion;
19 | private final String ciNcPluginVersion;
20 | private final String buildId;
21 | private final String buildConfigurationName;
22 | private final String buildURL;
23 | private final boolean buildHasChange;
24 | private final String versionControlName;
25 | private final String committer;
26 | private final String vcsVersion;
27 | private final String ciTimestamp;
28 | private String rootURL = "";
29 |
30 | public VCSCommit(Run, ?> build, ChangeLogSet> changelog) {
31 | buildId = String.valueOf(build.number);
32 | buildConfigurationName = build.getParent().getName();
33 | buildURL = getBuildURL(build);
34 |
35 | SimpleDateFormat iso8601DateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
36 |
37 | buildHasChange = changelog != null && !changelog.isEmptySet();
38 | if (buildHasChange) {
39 | versionControlName = changelog.getKind();
40 |
41 | final ChangeLogSet.Entry change = (ChangeLogSet.Entry) changelog.getItems()[0];
42 | long ciTimestampInMilliseconds = change.getTimestamp();
43 | ciTimestamp = iso8601DateTimeFormat.format(new Date(ciTimestampInMilliseconds));
44 |
45 | final User author = change.getAuthor();
46 | vcsVersion = change.getCommitId();
47 |
48 | String fullName = author.getFullName();
49 | String displayName = author.getDisplayName();
50 | if (AppCommon.isValidEmailAddress(fullName)) {
51 | committer = fullName;
52 | } else if (AppCommon.isValidEmailAddress(displayName)) {
53 | committer = displayName;
54 | } else {
55 | committer = fullName;
56 | }
57 |
58 | } else {
59 | versionControlName = "";
60 | ciTimestamp = iso8601DateTimeFormat.format(new Date());
61 | vcsVersion = "";
62 | committer = "";
63 | }
64 |
65 | VersionNumber versionNumber = Jenkins.getVersion();
66 | ciBuildServerVersion = versionNumber != null ? versionNumber.toString() : "Not found.";
67 | ciNcPluginVersion = null; // don't add plugin version number
68 | }
69 |
70 | public static VCSCommit empty(Run, ?> build) {
71 | return new VCSCommit(build, null);
72 | }
73 |
74 | public void setRootURL(String rootURL) {
75 | if (rootURL == null) {
76 | this.rootURL = "";
77 | return;
78 | }
79 | this.rootURL = rootURL;
80 | }
81 |
82 | public void addVcsCommitInfo(List params) {
83 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiBuildId", buildId));
84 | params.add(new BasicNameValuePair("VcsCommitInfoModel.IntegrationSystem", "Jenkins"));
85 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiBuildServerVersion",
86 | ciBuildServerVersion));
87 | params.add(
88 | new BasicNameValuePair("VcsCommitInfoModel.CiNcPluginVersion", ciNcPluginVersion));
89 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiBuildConfigurationName",
90 | buildConfigurationName));
91 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiBuildUrl", rootURL + buildURL));
92 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiBuildHasChange",
93 | String.valueOf(buildHasChange)));
94 | params.add(new BasicNameValuePair("VcsCommitInfoModel.CiTimestamp", ciTimestamp));
95 | params.add(new BasicNameValuePair("VcsCommitInfoModel.VcsName", versionControlName));
96 | params.add(new BasicNameValuePair("VcsCommitInfoModel.VcsVersion", vcsVersion));
97 | params.add(new BasicNameValuePair("VcsCommitInfoModel.Committer", committer));
98 | }
99 |
100 | private String getBuildURL(Run, ?> build) {
101 |
102 | try {
103 | return build.getUrl();
104 | } catch (Exception ex) {
105 | return "";
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanReport.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.acunetix.utility.AppCommon;
4 | import org.apache.hc.core5.http.ClassicHttpResponse;
5 | import org.json.simple.JSONObject;
6 | import org.json.simple.parser.JSONParser;
7 | import org.json.simple.parser.ParseException;
8 |
9 | import java.io.IOException;
10 |
11 | public class ScanReport {
12 | private final ClassicHttpResponse reportRequestResponse;
13 | private final boolean scanRequestHasError;
14 | private final String scanRequestErrorMessage;
15 | private final boolean reportRequestHasError;
16 | private final String reportRequestErrorMessage;
17 | private final String requestURI;
18 | private static String reportHtmlAsString = null;
19 |
20 | public ScanReport(ClassicHttpResponse reportRequestResponse, String requestURI) {
21 | this.reportRequestResponse = reportRequestResponse;
22 | this.scanRequestHasError = false;
23 | this.scanRequestErrorMessage = "";
24 | this.reportRequestHasError = false;
25 | this.reportRequestErrorMessage = "";
26 | this.requestURI = requestURI;
27 | }
28 |
29 | public ScanReport(boolean scanRequestHasError, String scanRequestErrorMessage,
30 | boolean reportRequestHasError, String reportRequestErrorMessage, String requestURI) {
31 | this.reportRequestResponse = null;
32 | this.scanRequestHasError = scanRequestHasError;
33 | this.scanRequestErrorMessage = scanRequestErrorMessage;
34 | this.reportRequestHasError = reportRequestHasError;
35 | this.reportRequestErrorMessage = reportRequestErrorMessage;
36 | this.requestURI = requestURI;
37 | }
38 |
39 | private String getContentType() {
40 | return reportRequestResponse.getHeaders("Content-Type")[0].getValue();
41 | }
42 |
43 | public boolean isReportGenerated() {
44 | // when report stored, it will be loaded from disk for later requests. There is an exception
45 | // potential.
46 | try {
47 | return getContentType().equalsIgnoreCase("text/html");
48 | } catch (Exception ex) {
49 | return false;
50 | }
51 | }
52 |
53 | public static void setReportHtmlAsStringField(String reportHtml) {
54 | ScanReport.reportHtmlAsString = reportHtml;
55 | }
56 |
57 | public void setReportHtmlAsString(String reportHtml) {
58 | setReportHtmlAsStringField(reportHtml);
59 | }
60 |
61 | public String getContent() {
62 | String content = "";
63 | try {
64 | if (scanRequestHasError) {
65 | content = ExceptionContent(content, scanRequestErrorMessage);
66 | } else if (reportRequestHasError) {
67 | content = ExceptionContent(content, reportRequestErrorMessage);
68 | } else {
69 |
70 | String contentData = null;
71 |
72 | try {
73 | contentData = AppCommon.parseResponseToString(reportRequestResponse);
74 |
75 | setReportHtmlAsString(contentData);
76 | } catch (IOException ex) {
77 | contentData = reportHtmlAsString;
78 | }
79 |
80 | if (isReportGenerated()) {
81 | content = contentData;
82 | } else {
83 | JSONParser parser = new JSONParser();
84 | JSONObject obj = (JSONObject) parser.parse(contentData);
85 | content = (String) obj.get("Message");
86 | }
87 | }
88 | } catch (ParseException ex) {
89 | content = ExceptionContent("Report result is not parsable.", ex.toString());
90 | } catch (Exception ex) {
91 | content = ExceptionContent(content, ex.toString());
92 | }
93 |
94 | return content;
95 | }
96 |
97 | private String ExceptionContent(String content, String ExceptionMessage) {
98 | if (content != null && !content.isEmpty()) {
99 | content = "" + content + "
";
100 | } else {
101 | content = "Something went wrong.
";
102 | }
103 | if (requestURI != null) {
104 | content = content + "Request URL: " + requestURI + "
";
105 | }
106 | if (reportRequestResponse != null) {
107 | content = content + "HttpStatusCode: "
108 | + reportRequestResponse.getCode() + "
";
109 | }
110 | if (ExceptionMessage != null) {
111 | content = content + "Exception Message:: " + ExceptionMessage + "
";
112 | }
113 |
114 | return content;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/IgnoredVulnerabilityStateFilters.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import org.json.simple.JSONObject;
4 | import org.json.simple.parser.JSONParser;
5 | import org.json.simple.parser.ParseException;
6 |
7 | public class IgnoredVulnerabilityStateFilters{
8 |
9 | private String filtersString;
10 | private JSONObject filtersJsonObject;
11 | private Boolean present;
12 | private Boolean fixedUnconfirmed;
13 | private Boolean fixedCantRetest;
14 | private Boolean fixedConfirmed;
15 | private Boolean revived;
16 | private Boolean scanning;
17 | private Boolean ignored;
18 | private Boolean acceptedRisk;
19 | private Boolean falsePositive;
20 |
21 | public IgnoredVulnerabilityStateFilters(){
22 | assignAllToFalse();
23 | }
24 |
25 | // GETTERS & SETTERS
26 |
27 | public String getFiltersString(){
28 | return filtersString;
29 | }
30 |
31 | public void setFiltersString() {
32 | JSONObject object = new JSONObject();
33 | object.put("present" , this.present);
34 | object.put("fixedUnconfirmed" , this.fixedUnconfirmed);
35 | object.put("fixedCantRetest" , this.fixedCantRetest);
36 | object.put("fixedConfirmed" , this.fixedConfirmed);
37 | object.put("revived" , this.revived);
38 | object.put("scanning" , this.scanning);
39 | object.put("ignored" , this.ignored);
40 | object.put("acceptedRisk" , this.acceptedRisk);
41 | object.put("falsePositive" , this.falsePositive);
42 | this.filtersJsonObject = object;
43 | this.filtersString = object.toJSONString();
44 | }
45 |
46 | public JSONObject getFiltersJsonObject() {
47 | return filtersJsonObject;
48 | }
49 |
50 | public void setFiltersJsonObject() throws ParseException {
51 | JSONParser jsonParser = new JSONParser();
52 | this.filtersJsonObject = (JSONObject) jsonParser.parse(getFiltersString());
53 | }
54 |
55 | public Boolean getPresent() {
56 | return present;
57 | }
58 |
59 | public void setPresent(Boolean present) {
60 | this.present = present;
61 | }
62 |
63 | public Boolean getFixedUnconfirmed() {
64 | return fixedUnconfirmed;
65 | }
66 |
67 | public void setFixedUnconfirmed(Boolean fixedUnconfirmed) {
68 | this.fixedUnconfirmed = fixedUnconfirmed;
69 | }
70 |
71 | public Boolean getFixedCantRetest() {
72 | return fixedCantRetest;
73 | }
74 |
75 | public void setFixedCantRetest(Boolean fixedCantRetest) {
76 | this.fixedCantRetest = fixedCantRetest;
77 | }
78 |
79 | public Boolean getFixedConfirmed() {
80 | return fixedConfirmed;
81 | }
82 |
83 | public void setFixedConfirmed(Boolean fixedConfirmed) {
84 | this.fixedConfirmed = fixedConfirmed;
85 | }
86 |
87 | public Boolean getRevived() {
88 | return revived;
89 | }
90 |
91 | public void setRevived(Boolean revived) {
92 | this.revived = revived;
93 | }
94 |
95 | public Boolean getScanning() {
96 | return scanning;
97 | }
98 |
99 | public void setScanning(Boolean scanning) {
100 | this.scanning = scanning;
101 | }
102 |
103 | public Boolean getIgnored() {
104 | return ignored;
105 | }
106 |
107 | public void setIgnored(Boolean ignored) {
108 | this.ignored = ignored;
109 | }
110 |
111 | public Boolean getAcceptedRisk() {
112 | return acceptedRisk;
113 | }
114 |
115 | public void setAcceptedRisk(Boolean acceptedRisk) {
116 | this.acceptedRisk = acceptedRisk;
117 | }
118 |
119 | public Boolean getFalsePositive() {
120 | return falsePositive;
121 | }
122 |
123 | public void setFalsePositive(Boolean falsePositive) {
124 | this.falsePositive = falsePositive;
125 | }
126 |
127 | private void assignAllToFalse(){
128 | this.present = false;
129 | this.fixedUnconfirmed = false;
130 | this.fixedCantRetest = false;
131 | this.fixedConfirmed = false;
132 | this.revived = false;
133 | this.scanning = false;
134 | this.ignored = false;
135 | this.acceptedRisk = false;
136 | this.falsePositive = false;
137 | }
138 |
139 | private void assignAllToTrue(){
140 | this.present = true;
141 | this.fixedUnconfirmed = true;
142 | this.fixedCantRetest = true;
143 | this.fixedConfirmed = true;
144 | this.revived = true;
145 | this.scanning = true;
146 | this.ignored = true;
147 | this.acceptedRisk = true;
148 | this.falsePositive = true;
149 | }
150 |
151 | }
152 |
153 |
154 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/model/ScanRequestResult.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.model;
2 |
3 | import com.acunetix.utility.AppCommon;
4 | import hudson.util.Secret;
5 | import org.apache.hc.core5.http.HttpHeaders;
6 | import org.apache.hc.core5.http.ClassicHttpResponse;
7 | import org.apache.hc.client5.http.classic.HttpClient;
8 | import org.apache.hc.client5.http.classic.methods.HttpGet;
9 | import org.json.simple.parser.ParseException;
10 |
11 | import java.io.IOException;
12 | import java.net.MalformedURLException;
13 | import java.net.URI;
14 | import java.net.URISyntaxException;
15 | import java.net.URL;
16 | import java.util.Date;
17 | import java.util.HashMap;
18 | import java.util.Map;
19 |
20 | public class ScanRequestResult extends ScanRequestBase {
21 | public static ScanRequestResult errorResult(String errorMessage) {
22 | return new ScanRequestResult(errorMessage);
23 | }
24 |
25 | private String scanReportEndpoint;
26 |
27 | private final int httpStatusCode;
28 | private String data;
29 | private String scanTaskID;
30 | private boolean isError;
31 | private String errorMessage;
32 |
33 | // Response from Acunetix 360 API
34 | private ScanReport report = null;
35 | private Date previousRequestTime;
36 |
37 | private ScanRequestResult(String errorMessage) {
38 | super();
39 | this.errorMessage = errorMessage;
40 | httpStatusCode = 0;
41 | isError = true;
42 | data = "";
43 | }
44 |
45 | public ScanRequestResult(ClassicHttpResponse response, String apiURL, Secret apiToken, String ncReportType,
46 | ProxyBlock proxy)
47 | throws MalformedURLException, URISyntaxException {
48 | super(apiURL, apiToken, proxy);
49 | httpStatusCode = response.getCode();
50 | isError = httpStatusCode != 201;
51 |
52 | if (!isError) {
53 | try {
54 | data = AppCommon.parseResponseToString(response);
55 | isError = !(boolean) AppCommon.parseJsonValue(data, "IsValid");
56 | if (!isError) {
57 | scanTaskID = (String) AppCommon.parseJsonValue(data, "ScanTaskId");
58 | } else {
59 | errorMessage = (String) AppCommon.parseJsonValue(data, "ErrorMessage");
60 | }
61 | } catch (ParseException ex) {
62 | isError = true;
63 | errorMessage = "Scan request result is not parsable::: " + ex.toString();
64 | } catch (IOException ex) {
65 | isError = true;
66 | errorMessage = "Scan request result is not readable::: " + ex.toString();
67 | }
68 | }
69 |
70 | String scanReportRelativeUrl = "api/1.0/scans/report/";
71 | URI scanReportEndpointUri = new URL(ApiURL, scanReportRelativeUrl).toURI();
72 |
73 | Map queryparams = new HashMap<>();
74 | String reportType = ncReportType == null || ncReportType.equals("null") ? "ExecutiveSummary" : ncReportType;
75 | queryparams.put("Type", reportType);
76 | queryparams.put("Format", "Html");
77 | queryparams.put("Id", scanTaskID);
78 |
79 | scanReportEndpoint =
80 | scanReportEndpointUri.toString() + "?" + AppCommon.mapToQueryString(queryparams);
81 | }
82 |
83 | public String getScanTaskId() {
84 | return scanTaskID;
85 | }
86 |
87 | public int getHttpStatusCode() {
88 | return httpStatusCode;
89 | }
90 |
91 | public String getErrorMessage() {
92 | return errorMessage;
93 | }
94 |
95 | public boolean isError() {
96 | return isError;
97 | }
98 |
99 | public boolean isReportGenerated() {
100 | // If scan request is failed we don't need additional check.
101 | if (isError()) {
102 | return false;
103 | } else if (isReportAvailable()) {
104 | return true;
105 | } else if (canAskForReportFromNCCloud()) {// If report is not requested or report wasn't
106 | // ready in previous request we must check again.
107 | try {
108 | final ScanReport report = getReport();
109 | return report.isReportGenerated();
110 | } catch (Exception ex) {
111 | return false;
112 | }
113 | } else {
114 | return false;
115 | }
116 | }
117 |
118 | public ScanReport getReport() {
119 | // if report is not generated and requested yet, request it from server.
120 | if (canAskForReportFromNCCloud()) {
121 | final ScanReport reportFromNcCloud = getReportFromNcCloud();
122 | previousRequestTime = new Date();
123 |
124 | this.report = reportFromNcCloud;
125 |
126 | return this.report;
127 | }
128 |
129 | return report;
130 | }
131 |
132 | private boolean canAskForReportFromNCCloud() {
133 | Date now = new Date();
134 | // Is report not requested or have request threshold passed
135 | // And report isn't generated yet
136 | boolean isTimeThresholdPassed = previousRequestTime == null
137 | || now.getTime() - previousRequestTime.getTime() >= 60 * 1000;// 1 min
138 |
139 | return isTimeThresholdPassed || !isReportAvailable();
140 | }
141 |
142 | private boolean isReportAvailable() {
143 | return report != null && report.isReportGenerated();
144 | }
145 |
146 | private ScanReport getReportFromNcCloud() {
147 | ScanReport reportFromApi;
148 |
149 | if (!isError) {
150 | try {
151 | final HttpClient httpClient = getHttpClient();
152 | final HttpGet httpGet = new HttpGet(scanReportEndpoint);
153 | httpGet.setHeader(HttpHeaders.AUTHORIZATION, getAuthHeader());
154 |
155 | ClassicHttpResponse response = (ClassicHttpResponse) httpClient.execute(httpGet);
156 |
157 | reportFromApi = new ScanReport(response, scanReportEndpoint);
158 | } catch (IOException ex) {
159 | String reportRequestErrorMessage =
160 | "Report result is not readable::: " + ex.toString();
161 | reportFromApi = new ScanReport(false, "", true, reportRequestErrorMessage,
162 | scanReportEndpoint);
163 | }
164 | } else {
165 | reportFromApi = new ScanReport(true, errorMessage, false, "", scanReportEndpoint);
166 | }
167 |
168 | this.report = reportFromApi;
169 |
170 | return reportFromApi;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/utility/AppCommon.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.utility;
2 |
3 | import com.cloudbees.hudson.plugins.folder.Folder;
4 | import com.cloudbees.plugins.credentials.CredentialsMatcher;
5 | import com.cloudbees.plugins.credentials.CredentialsMatchers;
6 | import com.cloudbees.plugins.credentials.CredentialsProvider;
7 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
8 | import com.cloudbees.plugins.credentials.domains.DomainRequirement;
9 | import hudson.model.Item;
10 | import hudson.model.ItemGroup;
11 | import hudson.security.ACL;
12 | import jenkins.model.Jenkins;
13 | import org.apache.commons.validator.routines.UrlValidator;
14 | import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler;
15 | import org.apache.hc.core5.http.ClassicHttpResponse;
16 | import org.json.simple.JSONArray;
17 | import org.json.simple.JSONObject;
18 | import org.json.simple.parser.JSONParser;
19 | import org.json.simple.parser.ParseException;
20 |
21 | import jakarta.mail.internet.AddressException;
22 | import jakarta.mail.internet.InternetAddress;
23 | import java.io.*;
24 | import java.net.MalformedURLException;
25 | import java.net.URL;
26 | import java.net.URLDecoder;
27 | import java.net.URLEncoder;
28 | import java.nio.charset.StandardCharsets;
29 | import java.util.*;
30 |
31 |
32 | public class AppCommon {
33 | public static List getNames(Class extends Enum>> e) {
34 | String[] enumNames =
35 | Arrays.toString(e.getEnumConstants()).replaceAll("^.|.$", "").split(", ");
36 | return Arrays.asList(enumNames);
37 | }
38 |
39 | public static boolean isUrlValid(String url) {
40 | String[] schemes = {"http", "https"}; // DEFAULT schemes = "http", "https", "ftp"
41 | UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS);
42 |
43 | if (urlValidator.isValid(url)) {
44 | return true;
45 | } else {
46 | return false;
47 | }
48 | }
49 |
50 | public static boolean isValidEmailAddress(String email) {
51 | boolean result = true;
52 | try {
53 | InternetAddress emailAddr = new InternetAddress(email);
54 | emailAddr.validate();
55 | } catch (AddressException ex) {
56 | result = false;
57 | }
58 | return result;
59 | }
60 |
61 | public static boolean isGUIDValid(String guid) {
62 | try {
63 | if (guid == null) {
64 | return false;
65 | }
66 | UUID.fromString(
67 | // fixes the guid if it doesn't contain hypens
68 | guid.replaceFirst(
69 | "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)",
70 | "$1-$2-$3-$4-$5"));
71 | return true;
72 | } catch (IllegalArgumentException exception) {
73 | return false;
74 | }
75 | }
76 |
77 | public static URL getBaseURL(String url) throws MalformedURLException {
78 | return new URL(new URL(url), "/");
79 | }
80 |
81 | public static String mapToQueryString(Map map) {
82 | StringBuilder stringBuilder = new StringBuilder();
83 | String key;
84 | String value;
85 | for (Map.Entry entry : map.entrySet()) {
86 | if (stringBuilder.length() > 0) {
87 | stringBuilder.append("&");
88 | }
89 | try {
90 | key = entry.getKey();
91 | value = entry.getValue();
92 | stringBuilder.append((key != null ? URLEncoder.encode(key, "UTF-8") : ""));
93 | stringBuilder.append("=");
94 | stringBuilder.append(value != null ? URLEncoder.encode(value, "UTF-8") : "");
95 | } catch (UnsupportedEncodingException e) {
96 | throw new RuntimeException("This method requires UTF-8 encoding support", e);
97 | }
98 | }
99 |
100 | return stringBuilder.toString();
101 | }
102 |
103 | public static Map queryStringToMap(String input) {
104 | Map map = new HashMap<>();
105 |
106 | String[] nameValuePairs = input.split("&");
107 | for (String nameValuePair : nameValuePairs) {
108 | String[] nameValue = nameValuePair.split("=");
109 | try {
110 | map.put(URLDecoder.decode(nameValue[0], "UTF-8"),
111 | nameValue.length > 1 ? URLDecoder.decode(nameValue[1], "UTF-8") : "");
112 | } catch (UnsupportedEncodingException e) {
113 | throw new RuntimeException("This method requires UTF-8 encoding support", e);
114 | }
115 | }
116 |
117 | return map;
118 | }
119 |
120 | public static Object parseJsonValue(String Data, String key) throws ParseException {
121 | JSONParser parser = new JSONParser();
122 | Object parsedData = parser.parse(Data);
123 | Object value;
124 | if (parsedData instanceof JSONArray) {
125 | JSONArray array = (JSONArray) parsedData;
126 | JSONObject object = (JSONObject) array.get(0);
127 | value = object.get(key);
128 | } else {
129 | JSONObject obj = (JSONObject) parsedData;
130 | value = obj.get(key);
131 | }
132 | return value;
133 | }
134 |
135 | public static String parseResponseToString(ClassicHttpResponse response) throws IOException {
136 | final BasicHttpClientResponseHandler handler = new BasicHttpClientResponseHandler();
137 |
138 | return handler.handleResponse(response);
139 | }
140 |
141 | public static StandardUsernamePasswordCredentials findCredentialsById(
142 | final String credentialsId, final String descriptorUrlOrJobUrl)
143 | throws UnsupportedEncodingException {
144 | final Jenkins jenkins = Jenkins.get();
145 |
146 | /*
147 | * This case is without folder plugin "job/Project Name"
148 | */
149 | ItemGroup credentialsContext = (ItemGroup) jenkins;
150 |
151 | List folders = jenkins.getItems(Folder.class);
152 |
153 | /*
154 | * if folders are used then find project's context first
155 | */
156 | if (folders != null && folders.size() > 0) {
157 |
158 | List folderNames = getFolderNames(descriptorUrlOrJobUrl);
159 |
160 | folderNames.removeIf(item -> item == null || "".equals(item));
161 |
162 | // last one is project name so remove it
163 | folderNames.remove(folderNames.size() - 1);
164 |
165 | Folder deepestFolder = getDeepestFolder(folders, folderNames);
166 |
167 | if (folders.size() > 0 && deepestFolder != null
168 | && CredentialsProvider.hasStores(deepestFolder)) {
169 | credentialsContext = (ItemGroup) deepestFolder;
170 | }
171 | }
172 |
173 | List matches =
174 | CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class,
175 | credentialsContext, ACL.SYSTEM, (DomainRequirement) null);
176 |
177 | final CredentialsMatcher matcher = CredentialsMatchers.withId(credentialsId);
178 | final StandardUsernamePasswordCredentials result =
179 | CredentialsMatchers.firstOrNull(matches, matcher);
180 |
181 | return result;
182 | }
183 |
184 | private static List getFolderNames(final String descriptorUrlOrJobUrl)
185 | throws UnsupportedEncodingException {
186 | List folderNames;
187 | String projectFullUrl = descriptorUrlOrJobUrl;
188 |
189 | // if its descriptorUrl
190 | if (descriptorUrlOrJobUrl.contains("/job/")) {
191 | /*
192 | * Format is like:
193 | *
194 | * "job/Folder Name/job/Project Name" or
195 | * "job/Folder Name/job/FolderInsideFolder/job/Project Name"
196 | */
197 | projectFullUrl =
198 | URLDecoder.decode(descriptorUrlOrJobUrl, StandardCharsets.UTF_8.toString());
199 |
200 | // substring url to make it starts with "job"
201 | if (!projectFullUrl.startsWith("/job")) {
202 | projectFullUrl = projectFullUrl.substring(projectFullUrl.indexOf("/job"));
203 | }
204 |
205 | projectFullUrl = projectFullUrl.replace("/job/", "/");
206 |
207 | }
208 |
209 | // then it's a job url similar to this
210 | // "Folder 1/Folder 1 - Folder 1/Folder 1 - Folder 1 - Folder 1/Child Project"
211 |
212 | folderNames = new java.util.ArrayList<>(java.util.Arrays.asList(projectFullUrl.split("/")));
213 |
214 | return folderNames;
215 | }
216 |
217 | private static Folder getDeepestFolder(List folders, List folderNames) {
218 | if (folders.size() == 0 || folderNames.size() == 0) {
219 | return null;
220 | }
221 | Folder deepestFolder = folders.get(0);
222 | Folder tempFolder;
223 |
224 | Optional f =
225 | folders.stream().filter(w -> w.getName().equals(folderNames.get(0))).findFirst();
226 | if (f.isPresent()) {
227 | tempFolder = f.get();
228 | if (CredentialsProvider.hasStores(tempFolder)) {
229 | deepestFolder = tempFolder;
230 | }
231 |
232 | for (int i = 1; i < folderNames.size(); i++) {
233 | if(tempFolder != null) {
234 | tempFolder = (Folder) tempFolder.getItem(folderNames.get(i));
235 | if (tempFolder != null && CredentialsProvider.hasStores(tempFolder)) {
236 | deepestFolder = tempFolder;
237 | }
238 | }
239 | }
240 | }
241 |
242 | return deepestFolder;
243 | }
244 |
245 | public static List findCredentials(Item own) {
246 |
247 | final List matches =
248 | CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class,
249 | own, ACL.SYSTEM, (DomainRequirement) null);
250 | return matches;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.jenkins-ci.plugins
6 | plugin
7 | 5.26
8 |
9 |
10 | org.jenkins-ci.plugins
11 | acunetix-360-scan
12 | 2.1.20-SNAPSHOT
13 | hpi
14 |
15 |
16 | 2.516.3
17 |
21 |
22 | Acunetix 360 Scan Plugin
23 | Allows users to start security scans via Acunetix 360.
24 |
25 |
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-compiler-plugin
31 | 3.13.0
32 |
33 |
34 |
35 |
36 |
37 | org.jenkins-ci.tools
38 | maven-hpi-plugin
39 | true
40 |
41 |
42 | org.apache.maven.plugins
43 | maven-compiler-plugin
44 |
45 | 17
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | io.jenkins.tools.bom
55 | bom-2.516.x
56 | 5353.v3dec76e40169
57 | import
58 | pom
59 |
60 |
61 |
62 |
63 |
64 | io.jenkins.plugins
65 | jakarta-mail-api
66 |
67 |
68 | io.jenkins.plugins
69 | jakarta-activation-api
70 |
71 |
72 | org.jenkins-ci
73 | symbol-annotation
74 | 1.23
75 |
76 |
77 | org.kohsuke.stapler
78 | stapler-adjunct-jquery
79 | 1.12.4-0
80 |
81 |
82 | org.kohsuke.stapler
83 | stapler
84 |
85 |
86 |
87 |
88 | org.kohsuke.stapler
89 | json-lib
90 | 2.4-jenkins-8
91 |
92 |
93 | commons-logging
94 | commons-logging
95 |
96 |
97 | commons-lang
98 | commons-lang
99 |
100 |
101 |
102 |
103 | org.jenkins-ci.plugins
104 | credentials
105 |
106 |
107 | org.jenkins-ci.plugins
108 | cloudbees-folder
109 |
110 |
111 | io.jenkins.plugins
112 | apache-httpcomponents-client-5-api
113 | 5.2.1-1.0
114 |
115 |
116 | commons-codec
117 | commons-codec
118 | 1.15
119 |
120 |
121 | commons-beanutils
122 | commons-beanutils
123 | 1.11.0
124 |
125 |
126 | commons-logging
127 | commons-logging
128 |
129 |
130 |
131 |
132 | org.apache.commons
133 | commons-collections4
134 | 4.4
135 |
136 |
137 | commons-validator
138 | commons-validator
139 | 1.7
140 |
141 |
142 | commons-logging
143 | commons-logging
144 |
145 |
146 |
147 |
148 | com.googlecode.json-simple
149 | json-simple
150 | 1.1.1
151 |
152 |
153 | org.jenkins-ci.plugins
154 | structs
155 |
156 |
157 | org.jenkins-ci.plugins.workflow
158 | workflow-step-api
159 |
160 |
161 | org.jenkins-ci.plugins.workflow
162 | workflow-cps
163 |
164 |
165 | org.jenkins-ci.plugins.workflow
166 | workflow-job
167 |
168 |
169 | org.jenkins-ci.plugins.workflow
170 | workflow-api
171 |
172 |
173 | org.jenkins-ci.plugins.workflow
174 | workflow-basic-steps
175 |
176 |
177 | org.jenkins-ci.plugins.workflow
178 | workflow-support
179 |
180 |
181 | org.jenkins-ci.plugins
182 | command-launcher
183 |
184 |
185 | org.ogce
186 | xpp3
187 | 1.1.6
188 |
189 |
190 | org.jenkins-ci.plugins
191 | script-security
192 |
193 |
194 | com.google.code.gson
195 | gson
196 | 2.9.1
197 |
198 |
199 | org.apache.commons
200 | commons-digester3
201 | 3.2
202 |
203 |
204 | commons-logging
205 | commons-logging
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | central
214 | https://repo.maven.apache.org/maven2/
215 |
216 |
217 | repo.jenkins-ci.org
218 | https://repo.jenkins-ci.org/public/
219 |
220 |
221 |
222 |
223 | central
224 | https://repo.maven.apache.org/maven2/
225 |
226 |
227 | repo.jenkins-ci.org
228 | https://repo.jenkins-ci.org/public/
229 |
230 |
231 |
232 | scm:git:https://github.com/jenkinsci/${project.artifactId}-plugin.git
233 | scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git
234 | https://github.com/jenkinsci/acunetix-360-scan-plugin
235 | acunetix-360-scan-2.1.19
236 |
237 |
238 |
239 | MIT License
240 | https://raw.githubusercontent.com/jenkinsci/acunetix-360-scan-plugin/master/LICENSE
241 |
242 |
243 |
244 | https://github.com/jenkinsci/acunetix-360-scan-plugin
245 |
246 |
247 |
248 | acunetix360
249 | Acunetix360
250 | support.integration@acunetix.com
251 |
252 |
253 |
--------------------------------------------------------------------------------
/src/main/resources/com/acunetix/plugin/ACXScanBuilder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | ${%Is Confirmed}
39 |
40 |
41 |
42 |
43 | ${%Do Not Fail The Build When Scan Contains False Positive}
44 |
45 |
46 |
47 |
48 | ${%Do Not Fail The Build When Scan Contains Accepted Risk}
49 |
50 |
51 |
52 |
53 | ${%Stop The Scan When Build Fails}
54 |
55 |
56 |
57 |
58 | ${%Stop The Scan When Build is Aborted}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
69 |
70 |
71 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/src/main/webapp/scripts/arrive.js:
--------------------------------------------------------------------------------
1 | /*globals jQuery,Window,HTMLElement,HTMLDocument,HTMLCollection,NodeList,MutationObserver */
2 | /*exported Arrive*/
3 | /*jshint latedef:false */
4 |
5 | /*
6 | * arrive.js
7 | * v2.5.2
8 | * https://github.com/uzairfarooq/arrive
9 | * MIT licensed
10 | *
11 | * Copyright (c) 2014-2024 Uzair Farooq
12 | */
13 | var Arrive = (function(window, $, undefined) {
14 |
15 | "use strict";
16 |
17 | if(!window.MutationObserver || typeof HTMLElement === 'undefined'){
18 | return; //for unsupported browsers
19 | }
20 |
21 | var arriveUniqueId = 0;
22 |
23 | var utils = (function() {
24 | var matches = HTMLElement.prototype.matches || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector
25 | || HTMLElement.prototype.msMatchesSelector;
26 |
27 | return {
28 | matchesSelector: function(elem, selector) {
29 | return elem instanceof HTMLElement && matches.call(elem, selector);
30 | },
31 | // to enable function overloading - By John Resig (MIT Licensed)
32 | addMethod: function (object, name, fn) {
33 | var old = object[ name ];
34 | object[ name ] = function(){
35 | if ( fn.length == arguments.length ) {
36 | return fn.apply( this, arguments );
37 | }
38 | else if ( typeof old == 'function' ) {
39 | return old.apply( this, arguments );
40 | }
41 | };
42 | },
43 | callCallbacks: function(callbacksToBeCalled, registrationData, mutationEvents) {
44 | if (!callbacksToBeCalled.length) return;
45 |
46 | if (registrationData && registrationData.options.onceOnly) {
47 | // as onlyOnce param is true, make sure we fire the event for only one item
48 | callbacksToBeCalled = [callbacksToBeCalled[0]];
49 |
50 | // unbind event after first callback as onceOnly is true.
51 | registrationData.me.unbindEventWithSelectorAndCallback.call(
52 | registrationData.target, registrationData.selector, registrationData.callback);
53 | }
54 |
55 | for (var i = 0, cb; (cb = callbacksToBeCalled[i]); i++) {
56 | if (cb && cb.callback) {
57 | cb.callback.call(cb.elem, cb.elem);
58 | }
59 | }
60 |
61 | if (registrationData && registrationData.callback && mutationEvents) {
62 | mutationEvents.addTimeoutHandler(registrationData.target, registrationData.selector, registrationData.callback, registrationData.options, registrationData.data);
63 | }
64 | },
65 | // traverse through all descendants of a node to check if event should be fired for any descendant
66 | checkChildNodesRecursively: function(nodes, registrationData, matchFunc, callbacksToBeCalled) {
67 | // check each new node if it matches the selector
68 | for (var i=0, node; (node = nodes[i]); i++) {
69 | if (matchFunc(node, registrationData, callbacksToBeCalled)) {
70 | callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
71 | }
72 |
73 | if (node.childNodes.length > 0) {
74 | utils.checkChildNodesRecursively(node.childNodes, registrationData, matchFunc, callbacksToBeCalled);
75 | }
76 | }
77 | },
78 | mergeArrays: function(firstArr, secondArr){
79 | // Overwrites default options with user-defined options.
80 | var options = {},
81 | attrName;
82 | for (attrName in firstArr) {
83 | if (firstArr.hasOwnProperty(attrName)) {
84 | options[attrName] = firstArr[attrName];
85 | }
86 | }
87 | for (attrName in secondArr) {
88 | if (secondArr.hasOwnProperty(attrName)) {
89 | options[attrName] = secondArr[attrName];
90 | }
91 | }
92 | return options;
93 | },
94 | toElementsArray: function (elements) {
95 | // check if object is an array (or array like object)
96 | // Note: window object has .length property but it's not array of elements so don't consider it an array
97 | if (typeof elements !== "undefined" && (typeof elements.length !== "number" || elements === window)) {
98 | elements = [elements];
99 | }
100 | return elements;
101 | }
102 | };
103 | })();
104 |
105 |
106 | // Class to maintain state of all registered events of a single type
107 | var EventsBucket = (function() {
108 | var EventsBucket = function() {
109 | // holds all the events
110 |
111 | this._eventsBucket = [];
112 | // function to be called while adding an event, the function should do the event initialization/registration
113 | this._beforeAdding = null;
114 | // function to be called while removing an event, the function should do the event destruction
115 | this._beforeRemoving = null;
116 | };
117 |
118 | EventsBucket.prototype.addEvent = function(target, selector, options, callback, data) {
119 | var newEvent = {
120 | target: target,
121 | selector: selector,
122 | options: options,
123 | callback: callback,
124 | data: data,
125 | firedElems: []
126 | };
127 |
128 | if (this._beforeAdding) {
129 | this._beforeAdding(newEvent);
130 | }
131 |
132 | this._eventsBucket.push(newEvent);
133 | return newEvent;
134 | };
135 |
136 | EventsBucket.prototype.removeEvent = function(compareFunction) {
137 | for (var i=this._eventsBucket.length - 1, registeredEvent; (registeredEvent = this._eventsBucket[i]); i--) {
138 | if (compareFunction(registeredEvent)) {
139 | if (this._beforeRemoving) {
140 | this._beforeRemoving(registeredEvent);
141 | }
142 |
143 | if (registeredEvent.data && registeredEvent.data.timeoutId) {
144 | clearTimeout(registeredEvent.data.timeoutId);
145 | }
146 |
147 | // mark callback as null so that even if an event mutation was already triggered it does not call callback
148 | var removedEvents = this._eventsBucket.splice(i, 1);
149 | if (removedEvents && removedEvents.length) {
150 | removedEvents[0].callback = null;
151 | }
152 | }
153 | }
154 | };
155 |
156 | EventsBucket.prototype.beforeAdding = function(beforeAdding) {
157 | this._beforeAdding = beforeAdding;
158 | };
159 |
160 | EventsBucket.prototype.beforeRemoving = function(beforeRemoving) {
161 | this._beforeRemoving = beforeRemoving;
162 | };
163 |
164 | return EventsBucket;
165 | })();
166 |
167 |
168 | /**
169 | * @constructor
170 | * General class for binding/unbinding arrive and leave events
171 | */
172 | var MutationEvents = function(getObserverConfig, onMutation) {
173 | var eventsBucket = new EventsBucket(),
174 | me = this;
175 |
176 | var defaultOptions = {
177 | fireOnAttributesModification: false
178 | };
179 |
180 | // actual event registration before adding it to bucket
181 | eventsBucket.beforeAdding(function(registrationData) {
182 | var
183 | target = registrationData.target,
184 | observer;
185 |
186 | // mutation observer does not work on window or document
187 | if (target === window.document || target === window) {
188 | target = document.getElementsByTagName("html")[0];
189 | }
190 |
191 | // Create an observer instance
192 | observer = new MutationObserver(function(e) {
193 | onMutation.call(this, e, registrationData);
194 | });
195 |
196 | var config = getObserverConfig(registrationData.options);
197 |
198 | observer.observe(target, config);
199 |
200 | registrationData.observer = observer;
201 | registrationData.me = me;
202 | });
203 |
204 | // cleanup/unregister before removing an event
205 | eventsBucket.beforeRemoving(function (eventData) {
206 | eventData.observer.disconnect();
207 | });
208 |
209 | this.bindEvent = function(selector, options, callback) {
210 | options = utils.mergeArrays(defaultOptions, options);
211 |
212 | var elements = utils.toElementsArray(this);
213 |
214 | for (var i = 0; i < elements.length; i++) {
215 | const data = {};
216 |
217 | // Add timeout handling
218 | me.addTimeoutHandler(elements[i], selector, callback, options, data);
219 |
220 | eventsBucket.addEvent(elements[i], selector, options, callback, data);
221 | }
222 | };
223 |
224 | this.unbindEvent = function() {
225 | var elements = utils.toElementsArray(this);
226 | eventsBucket.removeEvent(function(eventObj) {
227 | for (var i = 0; i < elements.length; i++) {
228 | if (this === undefined || eventObj.target === elements[i]) {
229 | return true;
230 | }
231 | }
232 | return false;
233 | });
234 | };
235 |
236 | this.unbindEventWithSelectorOrCallback = function(selector) {
237 | var elements = utils.toElementsArray(this),
238 | callback = selector,
239 | compareFunction;
240 |
241 | if (typeof selector === "function") {
242 | compareFunction = function(eventObj) {
243 | for (var i = 0; i < elements.length; i++) {
244 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.callback === callback) {
245 | return true;
246 | }
247 | }
248 | return false;
249 | };
250 | }
251 | else {
252 | compareFunction = function(eventObj) {
253 | for (var i = 0; i < elements.length; i++) {
254 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector) {
255 | return true;
256 | }
257 | }
258 | return false;
259 | };
260 | }
261 | eventsBucket.removeEvent(compareFunction);
262 | };
263 |
264 | this.unbindEventWithSelectorAndCallback = function(selector, callback) {
265 | var elements = utils.toElementsArray(this);
266 | eventsBucket.removeEvent(function(eventObj) {
267 | for (var i = 0; i < elements.length; i++) {
268 | if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector && eventObj.callback === callback) {
269 | return true;
270 | }
271 | }
272 | return false;
273 | });
274 | };
275 |
276 | this.addTimeoutHandler = function(target, selector, callback, options, data) {
277 | if (!options.timeout || options.timeout <= 0) {
278 | return;
279 | }
280 |
281 | if (data.timeoutId) {
282 | clearTimeout(data.timeoutId);
283 | }
284 |
285 | data.timeoutId = setTimeout(() => {
286 | me.unbindEventWithSelectorAndCallback.call(target, selector, callback);
287 | callback.call(null, null);
288 | }, options.timeout);
289 | }
290 |
291 | return this;
292 | };
293 |
294 |
295 | /**
296 | * @constructor
297 | * Processes 'arrive' events
298 | */
299 | var ArriveEvents = function() {
300 | // Default options for 'arrive' event
301 | var arriveDefaultOptions = {
302 | fireOnAttributesModification: false,
303 | onceOnly: false,
304 | existing: false,
305 | timeout: 0 // default 0 (no timeout)
306 | };
307 |
308 | function getArriveObserverConfig(options) {
309 | var config = {
310 | attributes: false,
311 | childList: true,
312 | subtree: true
313 | };
314 |
315 | if (options.fireOnAttributesModification) {
316 | config.attributes = true;
317 | }
318 |
319 | return config;
320 | }
321 |
322 | function onArriveMutation(mutations, registrationData) {
323 | mutations.forEach(function( mutation ) {
324 | var newNodes = mutation.addedNodes,
325 | targetNode = mutation.target,
326 | callbacksToBeCalled = [],
327 | node;
328 |
329 | // If new nodes are added
330 | if( newNodes !== null && newNodes.length > 0 ) {
331 | utils.checkChildNodesRecursively(newNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
332 | }
333 | else if (mutation.type === "attributes") {
334 | if (nodeMatchFunc(targetNode, registrationData, callbacksToBeCalled)) {
335 | callbacksToBeCalled.push({ callback: registrationData.callback, elem: targetNode });
336 | }
337 | }
338 |
339 | utils.callCallbacks(callbacksToBeCalled, registrationData, arriveEvents);
340 | });
341 | }
342 |
343 | function nodeMatchFunc(node, registrationData, callbacksToBeCalled) {
344 | // check a single node to see if it matches the selector
345 | if (utils.matchesSelector(node, registrationData.selector)) {
346 | if(node._id === undefined) {
347 | node._id = arriveUniqueId++;
348 | }
349 | // make sure the arrive event is not already fired for the element
350 | if (registrationData.firedElems.indexOf(node._id) == -1) {
351 | registrationData.firedElems.push(node._id);
352 |
353 | return true;
354 | }
355 | }
356 |
357 | return false;
358 | }
359 |
360 | arriveEvents = new MutationEvents(getArriveObserverConfig, onArriveMutation);
361 |
362 | var mutationBindEvent = arriveEvents.bindEvent;
363 |
364 | // override bindEvent function
365 | arriveEvents.bindEvent = function(selector, arg2, arg3) {
366 |
367 | var options = (typeof arg2 === 'object') ? utils.mergeArrays(arriveDefaultOptions, arg2) : { ...arriveDefaultOptions };
368 | var callback = (typeof arg3 === 'function') ? arg3 : (typeof arg2 === 'function') ? arg2 : undefined;
369 | var elements = utils.toElementsArray(this);
370 |
371 | // For promise and async support, we can only do onceOnly=true
372 | if (!callback)
373 | options.onceOnly = true;
374 |
375 | if (options.existing) {
376 | var existing = [];
377 |
378 | for (var i = 0; i < elements.length; i++) {
379 | var nodes = elements[i].querySelectorAll(selector);
380 | for (var j = 0; j < nodes.length; j++) {
381 | existing.push({ callback: callback, elem: nodes[j] });
382 | }
383 | }
384 |
385 | // no need to bind event if the callback has to be fired only once and we have already found the element
386 | if (options.onceOnly && existing.length) {
387 | if (callback) {
388 | return callback.call(existing[0].elem, existing[0].elem);
389 | } else {
390 | return Promise.resolve(existing[0].elem);
391 | }
392 | }
393 |
394 | setTimeout(utils.callCallbacks, 1, existing);
395 | }
396 |
397 | if (callback) {
398 | mutationBindEvent.call(this, selector, options, callback);
399 | } else {
400 | var a = this;
401 | return new Promise(resolve => mutationBindEvent.call(a, selector, options, resolve));
402 | }
403 | };
404 |
405 | return arriveEvents;
406 | };
407 |
408 |
409 | /**
410 | * @constructor
411 | * Processes 'leave' events
412 | */
413 | var LeaveEvents = function() {
414 | // Default options for 'leave' event
415 | var leaveDefaultOptions = {
416 | onceOnly: false,
417 | timeout: 0, // default 0 (no timeout)
418 | };
419 |
420 | function getLeaveObserverConfig() {
421 | var config = {
422 | childList: true,
423 | subtree: true
424 | };
425 |
426 | return config;
427 | }
428 |
429 | function onLeaveMutation(mutations, registrationData) {
430 | mutations.forEach(function( mutation ) {
431 | var removedNodes = mutation.removedNodes,
432 | callbacksToBeCalled = [];
433 |
434 | if( removedNodes !== null && removedNodes.length > 0 ) {
435 | utils.checkChildNodesRecursively(removedNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
436 | }
437 |
438 | utils.callCallbacks(callbacksToBeCalled, registrationData, leaveEvents);
439 | });
440 | }
441 |
442 | function nodeMatchFunc(node, registrationData) {
443 | return utils.matchesSelector(node, registrationData.selector);
444 | }
445 |
446 | leaveEvents = new MutationEvents(getLeaveObserverConfig, onLeaveMutation);
447 |
448 | var mutationBindEvent = leaveEvents.bindEvent;
449 |
450 | // override bindEvent function
451 | leaveEvents.bindEvent = function(selector, arg2, arg3) {
452 |
453 | var options = (typeof arg2 === 'object') ? utils.mergeArrays(leaveDefaultOptions, arg2) : { ...leaveDefaultOptions };
454 | var callback = (typeof arg3 === 'function') ? arg3 : (typeof arg2 === 'function') ? arg2 : undefined;
455 |
456 | if (callback) {
457 | mutationBindEvent.call(this, selector, options, callback);
458 | } else {
459 | // For promise and async support, we can only do onceOnly=true
460 | options.onceOnly = true;
461 |
462 | var a = this;
463 | return new Promise(resolve => mutationBindEvent.call(a, selector, options, resolve));
464 | }
465 | };
466 |
467 | return leaveEvents;
468 | };
469 |
470 |
471 |
472 | var arriveEvents = new ArriveEvents(),
473 | leaveEvents = new LeaveEvents();
474 |
475 | function exposeUnbindApi(eventObj, exposeTo, funcName) {
476 | // expose unbind function with function overriding
477 | utils.addMethod(exposeTo, funcName, eventObj.unbindEvent);
478 | utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorOrCallback);
479 | utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorAndCallback);
480 | }
481 |
482 | /*** expose APIs ***/
483 | function exposeApi(exposeTo) {
484 | exposeTo.arrive = arriveEvents.bindEvent;
485 | exposeUnbindApi(arriveEvents, exposeTo, "unbindArrive");
486 |
487 | exposeTo.leave = leaveEvents.bindEvent;
488 | exposeUnbindApi(leaveEvents, exposeTo, "unbindLeave");
489 | }
490 |
491 | if ($) {
492 | exposeApi($.fn);
493 | }
494 | exposeApi(HTMLElement.prototype);
495 | exposeApi(NodeList.prototype);
496 | exposeApi(HTMLCollection.prototype);
497 | exposeApi(HTMLDocument.prototype);
498 | exposeApi(Window.prototype);
499 |
500 | var Arrive = {};
501 | // expose functions to unbind all arrive/leave events
502 | exposeUnbindApi(arriveEvents, Arrive, "unbindAllArrive");
503 | exposeUnbindApi(leaveEvents, Arrive, "unbindAllLeave");
504 |
505 | return Arrive;
506 |
507 | })(window, typeof jQuery === 'undefined' ? null : jQuery, undefined);
508 |
--------------------------------------------------------------------------------
/acunetix-360-scan.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/src/main/java/com/acunetix/plugin/ACXScanBuilder.java:
--------------------------------------------------------------------------------
1 | package com.acunetix.plugin;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.io.StringWriter;
6 | import java.net.MalformedURLException;
7 | import java.net.URISyntaxException;
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 |
11 | import com.cloudbees.plugins.credentials.CredentialsProvider;
12 | import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
13 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
14 | import com.acunetix.model.IgnoredVulnerabilityStateFilters;
15 | import com.acunetix.model.ReportType;
16 | import com.acunetix.model.ScanCancelRequest;
17 | import com.acunetix.model.ScanCancelRequestResult;
18 | import com.acunetix.model.ScanInfoRequest;
19 | import com.acunetix.model.ScanInfoRequestResult;
20 | import com.acunetix.model.ScanRequest;
21 | import com.acunetix.model.ScanRequestResult;
22 | import com.acunetix.model.ScanTaskState;
23 | import com.acunetix.model.ScanType;
24 | import com.acunetix.model.VCSCommit;
25 | import com.acunetix.model.WebsiteModel;
26 | import com.acunetix.model.WebsiteModelRequest;
27 | import com.acunetix.model.WebsiteProfileModel;
28 | import com.acunetix.model.ProxyBlock;
29 | import com.acunetix.utility.AppCommon;
30 |
31 | import org.apache.commons.lang.StringUtils;
32 | import org.apache.hc.core5.http.ClassicHttpResponse;
33 | import org.jenkinsci.Symbol;
34 | import org.json.simple.parser.ParseException;
35 | import org.kohsuke.stapler.AncestorInPath;
36 | import org.kohsuke.stapler.DataBoundConstructor;
37 | import org.kohsuke.stapler.DataBoundSetter;
38 | import org.kohsuke.stapler.QueryParameter;
39 | import org.kohsuke.stapler.StaplerRequest;
40 | import org.kohsuke.stapler.bind.JavaScriptMethod;
41 | import org.kohsuke.stapler.verb.POST;
42 | import hudson.Extension;
43 | import hudson.FilePath;
44 | import hudson.Launcher;
45 | import hudson.model.AbstractProject;
46 | import hudson.model.Item;
47 | import hudson.model.ParameterValue;
48 | import hudson.model.ParametersAction;
49 | import hudson.model.Run;
50 | import hudson.model.TaskListener;
51 | import hudson.tasks.BuildStepDescriptor;
52 | import hudson.tasks.Builder;
53 | import hudson.util.FormValidation;
54 | import hudson.util.ListBoxModel;
55 | import hudson.util.Secret;
56 | import hudson.util.ListBoxModel.Option;
57 | import jenkins.model.Jenkins;
58 | import jenkins.tasks.SimpleBuildStep;
59 | import net.sf.json.JSONObject;
60 |
61 | public class ACXScanBuilder extends Builder implements SimpleBuildStep {
62 |
63 | private String ncScanType;
64 | private String ncWebsiteId;
65 | private String ncProfileId;
66 | private Secret ncApiToken;
67 | private String acxServerURL;
68 | private String credentialsId;
69 | private String ncSeverity;
70 | private Boolean ncStopScan;
71 | private Boolean ncConfirmed;
72 | private Boolean ncDoNotFail;
73 | private Boolean ncIgnoreFalsePositive;
74 | private Boolean ncIgnoreRiskAccepted;
75 | private IgnoredVulnerabilityStateFilters ncFilters = new IgnoredVulnerabilityStateFilters();
76 | private String ncReportType;
77 | private String ncScanTaskId;
78 | private Boolean ncAbortScan;
79 | private Boolean ncCancelEventFired;
80 | private Boolean useProxy;
81 | private String pHost;
82 | private String pPort;
83 | private String pUser;
84 | private String pPassword;
85 |
86 | private static final String apiTokenBuildParameterName = "APITOKEN";
87 |
88 | // Fields in config.jelly must match the parameter names in the
89 | // "DataBoundConstructor"
90 | // this ctor called when project's settings save method called
91 | @DataBoundConstructor
92 | public ACXScanBuilder(String ncScanType, String ncWebsiteId, String ncProfileId, Boolean ncDoNotFail, String ncReportType) {
93 | this.ncScanType = ncScanType == null ? "" : ncScanType;
94 | this.ncWebsiteId = ncWebsiteId == null ? "" : ncWebsiteId;
95 | this.ncProfileId = ncProfileId == null ? "" : ncProfileId;
96 | this.ncDoNotFail = ncDoNotFail;
97 | this.ncReportType = ncReportType == null || ncReportType.equals("null") ? "ExecutiveSummary" : ncReportType;
98 | }
99 |
100 | public String getNcSeverity() {
101 | return ncSeverity;
102 | }
103 |
104 | public Boolean getNcStopScan() {
105 | return ncStopScan;
106 | }
107 |
108 | public Boolean getNcConfirmed(){
109 | return ncConfirmed;
110 | }
111 |
112 | public Boolean getNcDoNotFail(){
113 | return ncDoNotFail;
114 | }
115 |
116 | public String getNcScanType() {
117 | return ncScanType;
118 | }
119 |
120 | public String getCredentialsId() {
121 | return credentialsId;
122 | }
123 |
124 | public IgnoredVulnerabilityStateFilters getFilters(){
125 | return ncFilters;
126 | }
127 |
128 | public void setFilters(){
129 | this.ncFilters = new IgnoredVulnerabilityStateFilters();
130 | this.ncFilters.setFalsePositive(this.ncIgnoreFalsePositive);
131 | this.ncFilters.setAcceptedRisk(this.ncIgnoreRiskAccepted);
132 | }
133 |
134 | @DataBoundSetter
135 | public void setCredentialsId(String credentialsId) {
136 | this.credentialsId = credentialsId;
137 | }
138 |
139 | public void setNcScanType(String ncScanType) {
140 | this.ncScanType = ncScanType;
141 | }
142 |
143 | public String getNcWebsiteId() {
144 | return ncWebsiteId;
145 | }
146 |
147 | public void setNcWebsiteId(String ncTargetURL) {
148 | this.ncWebsiteId = ncTargetURL;
149 | }
150 |
151 | public String getNcProfileId() {
152 | return ncProfileId;
153 | }
154 |
155 | public void setNcProfileId(String ncProfileId) {
156 | this.ncProfileId = ncProfileId;
157 | }
158 |
159 | public String getAcxServerURL() {
160 | return acxServerURL;
161 | }
162 |
163 | @DataBoundSetter
164 | public void setNcSeverity(String ncSeverity) {
165 | this.ncSeverity = ncSeverity;
166 | }
167 |
168 | @DataBoundSetter
169 | public void setNcStopScan(Boolean ncStopScan) {
170 | this.ncStopScan = ncStopScan;
171 | }
172 |
173 | @DataBoundSetter
174 | public void setNcConfirmed(Boolean ncConfirmed){
175 | this.ncConfirmed = ncConfirmed;
176 | }
177 |
178 | @DataBoundSetter
179 | public void setNcDoNotFail(Boolean ncDoNotFail){
180 | this.ncDoNotFail = ncDoNotFail;
181 | }
182 |
183 | @DataBoundSetter
184 | public void setAcxServerURL(String acxServerURL) {
185 | this.acxServerURL = acxServerURL;
186 | }
187 |
188 | public Secret getNcApiToken() {
189 | if (ncApiToken == null) {
190 | ncApiToken = getDescriptor().getNcApiToken();
191 | }
192 | return ncApiToken;
193 | }
194 |
195 | @DataBoundSetter
196 | public void setNcApiToken(Object ncApiToken) {
197 | if (ncApiToken.getClass() == String.class) {
198 | this.ncApiToken = Secret.fromString((String) ncApiToken);
199 | }
200 | if (ncApiToken.getClass() == Secret.class) {
201 | this.ncApiToken = (Secret) ncApiToken;
202 | }
203 | }
204 |
205 | public Boolean getNcIgnoreFalsePositive() {
206 | return ncIgnoreFalsePositive;
207 | }
208 |
209 | @DataBoundSetter
210 | public void setNcIgnoreFalsePositive(Boolean ncIgnoreFalsePositive) {
211 | this.ncIgnoreFalsePositive = ncIgnoreFalsePositive;
212 | }
213 |
214 | public Boolean getNcIgnoreRiskAccepted() {
215 | return ncIgnoreRiskAccepted;
216 | }
217 |
218 | @DataBoundSetter
219 | public void setNcIgnoreRiskAccepted(Boolean ncIgnoreRiskAccepted) {
220 | this.ncIgnoreRiskAccepted = ncIgnoreRiskAccepted;
221 | }
222 |
223 | public String getNcReportType(){
224 | return ncReportType;
225 | }
226 |
227 | @DataBoundSetter
228 | public void setNcReportType(String ncReportType) {
229 | this.ncReportType = ncReportType;
230 | }
231 |
232 | public void setScanTaskId(String ncScanTaskId) {
233 | this.ncScanTaskId = ncScanTaskId;
234 | }
235 |
236 | public String getScanTaskId() {
237 | return ncScanTaskId;
238 | }
239 |
240 | public void setCancelState(Boolean ncCancelEventFired) {
241 | this.ncCancelEventFired = ncCancelEventFired;
242 | }
243 |
244 | public Boolean getCancelState() {
245 | return ncCancelEventFired;
246 | }
247 |
248 | @DataBoundSetter
249 | public void setNcAbortScan(Boolean ncAbortScan) {
250 | this.ncAbortScan = ncAbortScan;
251 | }
252 |
253 | public Boolean getNcAbortScan() {
254 | return ncAbortScan;
255 | }
256 |
257 | @DataBoundSetter
258 | public void setUseProxy(Boolean useProxy) {
259 | this.useProxy = useProxy;
260 | }
261 |
262 | public Boolean getUseProxy() {
263 | return useProxy;
264 | }
265 |
266 | @DataBoundSetter
267 | public void setpHost(String pHost) {
268 | this.pHost = pHost;
269 | }
270 |
271 | public String getpHost() {
272 | return pHost;
273 | }
274 |
275 | @DataBoundSetter
276 | public void setpPort(String pPort) {
277 | this.pPort = pPort;
278 | }
279 |
280 | public String getpPort() {
281 | return pPort;
282 | }
283 |
284 | @DataBoundSetter
285 | public void setpUser(String pUser) {
286 | this.pUser = pUser;
287 | }
288 |
289 | public String getpUser() {
290 | return pUser;
291 | }
292 |
293 | @DataBoundSetter
294 | public void setpPassword(String pPassword) {
295 | this.pPassword = pPassword;
296 | }
297 |
298 | public String getpPassword() {
299 | return pPassword;
300 | }
301 |
302 | @Override
303 | public void perform(Run, ?> build, FilePath workspace, Launcher launcher,
304 | TaskListener listener) throws InterruptedException, IOException {
305 | logInfo("Scan step created...", listener);
306 |
307 | ACXScanSCMAction scmAction = build.getAction(ACXScanSCMAction.class);
308 | VCSCommit commit = scmAction == null ? VCSCommit.empty(build) : scmAction.getVcsCommit();
309 |
310 | try {
311 | ScanRequestHandler(build, commit, listener);
312 | }
313 | catch (RuntimeException e) {
314 | logInfo(e.getMessage(), listener);
315 | throw e; // Rethrow RuntimeException to handle it outside if necessary
316 | }
317 | catch(hudson.AbortException e)
318 | {
319 | try {
320 |
321 | DescriptorImpl descriptor = getDescriptor();
322 |
323 | String acxServerURL = StringUtils.isBlank(getAcxServerURL()) ? descriptor.getAcxServerURL()
324 | : getAcxServerURL();
325 |
326 | if (!StringUtils.isEmpty(credentialsId)) {
327 |
328 | // build.getEnvironment().get("job_name")
329 | // "Folder 1/Folder 1 - Folder 1/Folder 1 - Folder 1 - Folder 1/Child Project"
330 | final StandardUsernamePasswordCredentials credential = AppCommon.findCredentialsById(
331 | credentialsId, build.getEnvironment(listener).get("job_name"));
332 |
333 | if (credential != null) {
334 | acxServerURL = credential.getUsername();
335 | ncApiToken = credential.getPassword();
336 | }
337 | }
338 |
339 | // if token is not set, try to get from global variable or selected credential
340 | // from settings
341 | if (ncApiToken == null || ncApiToken.getPlainText().isEmpty()) {
342 | ncApiToken =
343 | getNcApiToken() != null && StringUtils.isBlank(getNcApiToken().getPlainText())
344 | ? descriptor.getNcApiToken()
345 | : getNcApiToken();
346 | }
347 |
348 | if (Secret.toString(ncApiToken) == ("$" + apiTokenBuildParameterName)) {
349 | ncApiToken = GetApiTokenFromBuildParameters(build);
350 | }
351 |
352 | ProxyBlock proxy = null;
353 | String pHost = null;
354 | String pPort = null;
355 | String pUser = null;
356 | String pPassword = null;
357 |
358 | Boolean useProxy = getUseProxy() == null ? descriptor.getUseProxy()
359 | : getUseProxy();
360 |
361 | if (useProxy != null && useProxy) {
362 | pHost = StringUtils.isBlank(getpHost()) ? descriptor.getpHost()
363 | : getpHost();
364 |
365 | pPort = StringUtils.isBlank(getpPort()) ? descriptor.getpPort()
366 | : getpPort();
367 |
368 | pUser = StringUtils.isBlank(getpUser()) ? descriptor.getpUser()
369 | : getpUser();
370 |
371 | pPassword = StringUtils.isBlank(getpPassword()) ? descriptor.getpPassword()
372 | : getpPassword();
373 |
374 | proxy = new ProxyBlock(useProxy, pHost, pPort, pUser, pPassword);
375 | }
376 |
377 | Boolean cancelScanWhenUserAbortsOperation = getNcAbortScan();
378 |
379 | if (cancelScanWhenUserAbortsOperation && !getCancelState()) {
380 | CancelScan(acxServerURL, ncApiToken, proxy, getScanTaskId() , listener);
381 | }
382 | }
383 | catch (RuntimeException ex) {
384 | logInfo(ex.getMessage(), listener);
385 | throw ex; // Rethrow RuntimeException to handle it outside if necessary
386 | }
387 | catch(Exception ex)
388 | {
389 | logInfo(ex.getMessage(), listener);
390 | }
391 |
392 | logInfo(e.getMessage(), listener);
393 | }
394 | catch (Exception e) {
395 | try {
396 | build.replaceAction(new ACXScanResultAction(
397 | ScanRequestResult.errorResult("Scan Request Failed:: " + e.getMessage())));
398 | } catch (Exception ex) {
399 | build.addAction(new ACXScanResultAction(
400 | ScanRequestResult.errorResult("Scan Request Failed:: " + e.getMessage())));
401 | }
402 |
403 | build.setResult(hudson.model.Result.FAILURE);
404 | }
405 | }
406 |
407 | private Secret GetApiTokenFromBuildParameters(Run, ?> build) {
408 | Secret secret = null;
409 |
410 | ParametersAction parametersAction = build.getAction(ParametersAction.class);
411 | if (parametersAction != null) {
412 |
413 | ParameterValue parameter = parametersAction.getAllParameters().stream()
414 | .filter(p -> p.getName().contains(apiTokenBuildParameterName)).findAny()
415 | .orElse(null);
416 |
417 | if (parameter != null && parameter.getValue() != null) {
418 | Object value = parameter.getValue();
419 | if (value != null && value.getClass() != null) {
420 | if (value.getClass() == Secret.class) {
421 | secret = (Secret) value;
422 | } else if (value.getClass() == String.class) {
423 | secret = Secret.fromString((String) value);
424 | }
425 | }
426 | }
427 | }
428 |
429 | return secret;
430 | }
431 |
432 | private void ScanRequestHandler(Run, ?> build, VCSCommit commit, TaskListener listener)
433 | throws Exception {
434 |
435 | DescriptorImpl descriptor = getDescriptor();
436 | String acxServerURL = StringUtils.isBlank(getAcxServerURL()) ? descriptor.getAcxServerURL()
437 | : getAcxServerURL();
438 |
439 | Secret ncApiToken = null;
440 |
441 | ProxyBlock proxy = null;
442 | String pHost = null;
443 | String pPort = null;
444 | String pUser = null;
445 | String pPassword = null;
446 |
447 | Boolean useProxy = getUseProxy() == null ? descriptor.getUseProxy()
448 | : getUseProxy();
449 |
450 | if (useProxy != null && useProxy) {
451 | pHost = StringUtils.isBlank(getpHost()) ? descriptor.getpHost()
452 | : getpHost();
453 |
454 | pPort = StringUtils.isBlank(getpPort()) ? descriptor.getpPort()
455 | : getpPort();
456 |
457 | pUser = StringUtils.isBlank(getpUser()) ? descriptor.getpUser()
458 | : getpUser();
459 |
460 | pPassword = StringUtils.isBlank(getpPassword()) ? descriptor.getpPassword()
461 | : getpPassword();
462 |
463 | proxy = new ProxyBlock(useProxy, pHost, pPort, pUser, pPassword);
464 | }
465 |
466 | // jenkin's server url
467 | String rootUrl = null;
468 |
469 | if (!StringUtils.isEmpty(credentialsId)) {
470 |
471 | // build.getEnvironment().get("job_name")
472 | // "Folder 1/Folder 1 - Folder 1/Folder 1 - Folder 1 - Folder 1/Child Project"
473 | final StandardUsernamePasswordCredentials credential = AppCommon.findCredentialsById(
474 | credentialsId, build.getEnvironment(listener).get("job_name"));
475 |
476 | if (credential != null) {
477 | acxServerURL = credential.getUsername();
478 | ncApiToken = credential.getPassword();
479 | }
480 | }
481 |
482 | // if token is not set, try to get from global variable or selected credential
483 | // from settings
484 | if (ncApiToken == null || ncApiToken.getPlainText().isEmpty()) {
485 | ncApiToken =
486 | getNcApiToken() != null && StringUtils.isBlank(getNcApiToken().getPlainText())
487 | ? descriptor.getNcApiToken()
488 | : getNcApiToken();
489 | }
490 |
491 | if (Secret.toString(ncApiToken) == ("$" + apiTokenBuildParameterName)) {
492 | ncApiToken = GetApiTokenFromBuildParameters(build);
493 | }
494 |
495 | // StringUtils.isEmpty checks null or empty
496 | if (StringUtils.isEmpty(rootUrl)) {
497 | rootUrl = descriptor.getRootURL();
498 | }
499 |
500 | commit.setRootURL(rootUrl);
501 |
502 | ScanRequest scanRequest = new ScanRequest(acxServerURL, ncApiToken, ncScanType, ncWebsiteId,
503 | ncProfileId, commit, proxy);
504 |
505 | logInfo("Requesting scan...", listener);
506 | ClassicHttpResponse scanRequestResponse = scanRequest.scanRequest();
507 | logInfo("Response status code: " + scanRequestResponse.getCode(),
508 | listener);
509 |
510 | ScanRequestResult scanRequestResult =
511 | new ScanRequestResult(scanRequestResponse, acxServerURL, ncApiToken, ncReportType,
512 | proxy);
513 | build.replaceAction(new ACXScanResultAction(scanRequestResult));
514 |
515 | setFilters();
516 |
517 | setScanTaskId(scanRequestResult.getScanTaskId());
518 |
519 | // HTTP status code 201 refers to created. This means our request added to
520 | // queue. Otherwise it is failed.
521 | if (scanRequestResult.getHttpStatusCode() == 201 && !scanRequestResult.isError()) {
522 | ScanRequestSuccessHandler(acxServerURL, ncApiToken, proxy,
523 | scanRequestResult, scanRequestResult.getScanTaskId(), ncDoNotFail, ncConfirmed,
524 | ncFilters, listener);
525 | } else {
526 | ScanRequestFailureHandler(scanRequestResult, listener);
527 | }
528 | }
529 |
530 | private void ScanRequestSuccessHandler(String acxServerURL, Secret ncApiToken, ProxyBlock proxy,
531 | ScanRequestResult scanRequestResult, String scanTaskId, Boolean doNotFail, Boolean isConfirmed,
532 | IgnoredVulnerabilityStateFilters filters, TaskListener listener)
533 | throws IOException, URISyntaxException, InterruptedException {
534 | logInfo("Scan requested successfully.", listener);
535 | ScanTaskState scanStatus = ScanTaskState.Queued;
536 | Boolean scanAbortedExternally = false;
537 | Boolean scanInfoConnectionError = false;
538 | Boolean isScanStarted = false;
539 | Boolean isSeverityBreaked = false;
540 |
541 | try {
542 | while (!scanStatus.equals(ScanTaskState.Complete)) {
543 | ScanInfoRequest scanInfoRequest =
544 | new ScanInfoRequest(acxServerURL, ncApiToken, scanTaskId, ncDoNotFail, ncConfirmed, ncFilters, proxy);
545 |
546 | logInfo("Requesting scan info...", listener);
547 | ClassicHttpResponse scanInfoRequestResponse = scanInfoRequest.scanInfoRequest();
548 | logInfo("Response scan info status code: "
549 | + scanInfoRequestResponse.getCode(), listener);
550 |
551 | ScanInfoRequestResult scanInfoRequestResult = new ScanInfoRequestResult(scanInfoRequestResponse);
552 |
553 | if (scanInfoRequestResult.isError()) {
554 | scanInfoConnectionError = true;
555 | logInfo("Get scan info error", listener);
556 | logError(scanInfoRequestResult.getErrorMessage(), listener);
557 | throw new hudson.AbortException("Error when getting scan info!");
558 | }
559 |
560 | scanStatus = scanInfoRequestResult.getScanTaskState();
561 |
562 | if (scanInfoRequestResult.checkSeverity(ncSeverity)) {
563 | isSeverityBreaked = true;
564 | String severityText = SeverityOptionsForBuildFailMesssage().get(ncSeverity);
565 | String failMessage = "Build failed because scan contains " + severityText + " severity!";
566 | logInfo(failMessage, listener);
567 | throw new hudson.AbortException(failMessage);
568 | }
569 |
570 | if (scanStatus.equals(ScanTaskState.Scanning) && !isScanStarted) {
571 | isScanStarted = true;
572 | logInfo("Scan started...", listener);
573 | } else if (scanStatus.equals(ScanTaskState.Failed)
574 | || scanStatus.equals(ScanTaskState.Cancelled)
575 | || scanStatus.equals(ScanTaskState.Paused)
576 | || scanStatus.equals(ScanTaskState.Pausing)) {
577 | scanAbortedExternally = true;
578 | logInfo("Scan aborted because state is " + scanStatus.toString(), listener);
579 | throw new hudson.AbortException("The scan was aborted outside of this instance");
580 | }
581 |
582 | Thread.sleep(10000);
583 | }
584 | logInfo("Scan completed...", listener);
585 | } catch (hudson.AbortException e) {
586 | Boolean isCancel = (scanInfoConnectionError || ((ncStopScan != null && ncStopScan)
587 | && (isSeverityBreaked || !scanAbortedExternally)));
588 | if (isCancel){
589 | CancelScan(acxServerURL, ncApiToken, proxy, scanTaskId, listener);
590 |
591 | setCancelState(true);
592 | }
593 | else{
594 | setCancelState(false);
595 | }
596 | throw new hudson.AbortException("The build was aborted");
597 | } catch (Exception e) {
598 | StringWriter errors = new StringWriter();
599 | e.printStackTrace(new PrintWriter(errors));
600 | logInfo(errors.toString(), listener);
601 | setCancelState(false);
602 | throw new hudson.AbortException("The build was aborted");
603 | }
604 | }
605 |
606 | private HashMap SeverityOptionsForBuildFailMesssage() {
607 | HashMap options = new HashMap();
608 | options.put("DoNotFail", "Do not fail the build");
609 | options.put("Critical", "Critical");
610 | options.put("Critical,High", "High or above");
611 | options.put("Critical,High,Medium", "Medium or above");
612 | options.put("Critical,High,Medium,Low", "Low or above");
613 | options.put("Critical,High,Medium,Low,Best Practice", "Best Practices or above");
614 | return options;
615 | }
616 |
617 | private void CancelScan(String acxServerURL, Secret ncApiToken, ProxyBlock proxy,
618 | String scanTaskId, TaskListener listener)
619 | throws IOException, MalformedURLException, NullPointerException, URISyntaxException {
620 |
621 | ScanCancelRequest scanCancelRequest = new ScanCancelRequest(acxServerURL, ncApiToken, scanTaskId, proxy);
622 |
623 | logInfo("Requesting scan cancel...", listener);
624 | ClassicHttpResponse scanCancelRequestResponse = scanCancelRequest.scanCancelRequest();
625 | logInfo("Response scan cancel status code: "
626 | + scanCancelRequestResponse.getCode(), listener);
627 |
628 | ScanCancelRequestResult scanCancelRequestResult = new ScanCancelRequestResult(scanCancelRequestResponse);
629 |
630 | if (scanCancelRequestResult.isError()) {
631 | logInfo("Scan cancel error", listener);
632 | logError(scanCancelRequestResult.getErrorMessage(), listener);
633 | throw new hudson.AbortException("Error when scan cancel!");
634 | } else {
635 | logInfo("Scan canceled Id:" + scanTaskId, listener);
636 | }
637 | }
638 |
639 | private void ScanRequestFailureHandler(
640 | ScanRequestResult scanRequestResult, TaskListener listener) throws Exception {
641 | logError("Scan request failed. Error Message: " + scanRequestResult.getErrorMessage(), listener);
642 |
643 | throw new Exception("Acunetix 360 Plugin: Failed to start the scan. Response status code: "
644 | + scanRequestResult.getHttpStatusCode());
645 | }
646 |
647 | private void logInfo(String message, TaskListener listener) {
648 | listener.getLogger().println("> Acunetix 360 Plugin: " + message);
649 | }
650 |
651 | private void logError(String message, TaskListener listener) {
652 | listener.error("> Acunetix 360 Plugin: " + message);
653 | }
654 |
655 | @Override
656 | public DescriptorImpl getDescriptor() {
657 | return (DescriptorImpl) super.getDescriptor();
658 | }
659 |
660 | @Symbol("ACXScanBuilder")
661 | @Extension
662 | public static final class DescriptorImpl extends BuildStepDescriptor {
663 | private long lastEditorId = 0;
664 | private ArrayList websiteModels = new ArrayList<>();
665 |
666 | private String acxServerURL;
667 | private Secret ncApiToken;
668 | private String rootURL;
669 | private Boolean useProxy;
670 | private String pHost;
671 | private String pPort;
672 | private String pUser;
673 | private String pPassword;
674 |
675 | public DescriptorImpl() {
676 | super(ACXScanBuilder.class);
677 | load();
678 |
679 | java.util.logging.Logger.getLogger(DescriptorImpl.class.getName()).info("Netsparker Descriptor loaded");
680 | }
681 |
682 | public String getAcxServerURL() {
683 | return acxServerURL;
684 | }
685 |
686 | public void setAcxServerURL(String acxServerURL) {
687 | this.acxServerURL = acxServerURL;
688 | }
689 |
690 |
691 | public Secret getNcApiToken() {
692 | return ncApiToken;
693 | }
694 |
695 | public void setNcApiToken(String ncApiToken) {
696 | this.ncApiToken = Secret.fromString(ncApiToken);
697 | }
698 |
699 | public String getRootURL() {
700 | return rootURL;
701 | }
702 |
703 | public Boolean getUseProxy() {
704 | return useProxy;
705 | }
706 |
707 | public void setUseProxy(Boolean useProxy) {
708 | this.useProxy = useProxy;
709 | }
710 |
711 | public String getpHost() {
712 | return pHost;
713 | }
714 |
715 | public void setpHost(String pHost) {
716 | this.pHost = pHost;
717 | }
718 |
719 | public String getpPort() {
720 | return pPort;
721 | }
722 |
723 | public void setpPort(String pPort) {
724 | this.pPort = pPort;
725 | }
726 |
727 | public String getpUser() {
728 | return pUser;
729 | }
730 |
731 | public void setpUser(String pUser) {
732 | this.pUser = pUser;
733 | }
734 |
735 | public String getpPassword() {
736 | return pPassword;
737 | }
738 |
739 | public void setpPassword(String pPassword) {
740 | this.pPassword = pPassword;
741 | }
742 |
743 | @Override
744 | public boolean isApplicable(Class extends AbstractProject> aClass) {
745 | // Indicates that this builder can be used with all kinds of project types
746 | java.util.logging.Logger.getLogger(DescriptorImpl.class.getName()).info("NCScanBuilder descriptor isApplicable called");
747 | return true;
748 | }
749 |
750 | @Override
751 | public String getDisplayName() {
752 | java.util.logging.Logger.getLogger(DescriptorImpl.class.getName()).info("NCScanBuilder getDisplayName called");
753 | return Messages.ACXScanBuilder_DescriptorImpl_DisplayName();
754 | }
755 |
756 | @Override
757 | // Invoked when the global configuration page is submitted.
758 | public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
759 | req.bindParameters(this);
760 | this.acxServerURL = formData.getString("acxServerURL");
761 | this.ncApiToken = Secret.fromString(formData.getString("ncApiToken"));
762 | this.useProxy = formData.getBoolean("useProxy");
763 | this.pHost = formData.getString("pHost");
764 | this.pPort = formData.getString("pPort");
765 | this.pUser = formData.getString("pUser");
766 | this.pPassword = formData.getString("pPassword");
767 | this.rootURL = Jenkins.get().getRootUrl();
768 |
769 | // To persist global configuration information, set properties and call save().
770 | save();
771 | return super.configure(req, formData);
772 | }
773 |
774 | @Override
775 | public String getConfigPage() {
776 | ProxyBlock proxy = null;
777 | if (useProxy != null && useProxy) {
778 | proxy = new ProxyBlock(useProxy, pHost, pPort, pUser, pPassword);
779 | }
780 |
781 | try {
782 | updateWebsiteModels(acxServerURL, ncApiToken, proxy);
783 | } catch (Exception e) {
784 | }
785 |
786 | return super.getConfigPage();
787 | }
788 |
789 | @SuppressWarnings("unused")
790 | public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item owner,
791 | @QueryParameter String credentialsId) {
792 | StandardListBoxModel listBoxModel = new StandardListBoxModel();
793 | listBoxModel.includeEmptyValue();
794 |
795 | if (owner == null) {
796 | if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
797 | return listBoxModel.includeCurrentValue(credentialsId);
798 | }
799 | } else {
800 | if (!owner.hasPermission(Item.EXTENDED_READ)
801 | && !owner.hasPermission(CredentialsProvider.USE_ITEM)) {
802 | return listBoxModel.includeCurrentValue(credentialsId);
803 | }
804 | }
805 |
806 | for (StandardUsernamePasswordCredentials credentialToAdd : AppCommon
807 | .findCredentials(owner)) {
808 | listBoxModel.with(credentialToAdd);
809 | }
810 |
811 | listBoxModel.includeCurrentValue(credentialsId);
812 |
813 | return listBoxModel;
814 | }
815 |
816 | @JavaScriptMethod
817 | @SuppressWarnings("unused")
818 | public synchronized String createEditorId() {
819 | return String.valueOf(lastEditorId++);
820 | }
821 |
822 | /*
823 | * methods named "doFill{fieldname}Items" need to be implemented for to fill select boxes in
824 | * UI
825 | */
826 | @SuppressWarnings("unused")
827 | public ListBoxModel doFillNcScanTypeItems() {
828 | ListBoxModel model = new ListBoxModel();
829 | model.add("Please Select Scan Type", "");
830 | model.add("Incremental", "Incremental");
831 | model.add("Full (With primary profile)", "FullWithPrimaryProfile");
832 | model.add("Full (With selected profile)", "FullWithSelectedProfile");
833 |
834 | return model;
835 | }
836 |
837 | @SuppressWarnings("unused")
838 | public ListBoxModel doFillNcWebsiteIdItems(@QueryParameter String credentialsId) {
839 |
840 |
841 | if (!StringUtils.isEmpty(credentialsId)) {
842 | doTestConnection(credentialsId);
843 | }
844 |
845 | ListBoxModel model = new ListBoxModel();
846 | model.add("Please select website","");
847 | for (WebsiteModel websiteModel : websiteModels) {
848 | model.add(websiteModel.getDisplayName(), websiteModel.getId());
849 | }
850 |
851 | return model;
852 | }
853 |
854 | @SuppressWarnings("unused")
855 | public ListBoxModel doFillNcProfileIdItems(@QueryParameter String ncWebsiteId) {
856 | WebsiteModel websiteModel = new WebsiteModel();
857 |
858 | for (WebsiteModel wm : websiteModels) {
859 | if (ncWebsiteId != null && wm.getId().equals(ncWebsiteId)) {
860 | websiteModel = wm;
861 | break;
862 | }
863 | }
864 |
865 | String placeholderText;
866 | final ArrayList websiteProfileModels = websiteModel.getProfiles();
867 | ListBoxModel model = new ListBoxModel();
868 |
869 | if (websiteProfileModels.isEmpty()) {
870 | placeholderText = "-- No profile found --";
871 | model.add(placeholderText, "");
872 | } else {
873 | model.add("Please Select Scan Profile","");
874 | for (WebsiteProfileModel websiteProfileModel : websiteProfileModels) {
875 | boolean isSelected = websiteProfileModels.size() == 1 ? true : false;
876 | model.add(new Option(websiteProfileModel.getName(), websiteProfileModel.getId(),isSelected));
877 | }
878 | }
879 |
880 | return model;
881 | }
882 |
883 | @SuppressWarnings("unused")
884 | public ListBoxModel doFillNcReportTypeItems() {
885 | ListBoxModel model = new ListBoxModel();
886 | model.add("Please Select Report Type","");
887 | model.add("Detailed Scan Report","ScanDetail");
888 | model.add("Executive Summary","ExecutiveSummary");
889 | model.add("Full Scan Detail","FullScanDetail");
890 | model.add("HIPAA Compliance","HIPAACompliance");
891 | model.add("ISO 27001 Compliance","Iso27001Compliance");
892 | model.add("Knowledge Base", "KnowledgeBase");
893 | model.add("OWASP Top Ten 2013","OwaspTopTen2013");
894 | model.add("OWASP Top Ten 2017","OwaspTopTen2017");
895 | model.add("PCI DSS Compliance","PCICompliance");
896 | model.add("SANS Top 25", "SansTop25");
897 | model.add("WASC Threat Classification","WASC");
898 |
899 | return model;
900 | }
901 |
902 | private int updateWebsiteModels(final String acxServerURL, final Secret ncApiToken,
903 | ProxyBlock proxy) throws IOException, URISyntaxException, ParseException {
904 | WebsiteModelRequest websiteModelRequest =
905 | new WebsiteModelRequest(acxServerURL, ncApiToken, proxy);
906 | final ClassicHttpResponse response = websiteModelRequest.getPluginWebSiteModels();
907 | int statusCode = response.getCode();
908 |
909 | if (statusCode == 200) {
910 | websiteModels = new ArrayList<>();
911 | websiteModels.addAll(websiteModelRequest.getWebsiteModels());
912 | }else if(statusCode == 401){
913 | websiteModels.clear();
914 | }
915 |
916 | return statusCode;
917 | }
918 |
919 | private FormValidation validateConnection(final String acxServerURL, final Secret ncApiToken,
920 | final ProxyBlock proxy) {
921 |
922 | try {
923 | int statusCode = updateWebsiteModels(acxServerURL, ncApiToken, proxy);
924 | if (statusCode == 200) {
925 | return FormValidation
926 | .ok("Successfully connected to the Acunetix 360.");
927 | } else {
928 | return FormValidation
929 | .error("Acunetix 360 rejected the request. HTTP status code: "
930 | + statusCode);
931 | }
932 | } catch (Exception e) {
933 | return FormValidation
934 | .error("Failed to connect to the Acunetix 360. : " + e.toString());
935 | }
936 | }
937 |
938 | @POST
939 | @SuppressWarnings("unused")
940 | public FormValidation doValidateAPI(@QueryParameter final String acxServerURL, @QueryParameter final Secret ncApiToken,
941 | @QueryParameter final Boolean useProxy,
942 | @QueryParameter final String pHost,
943 | @QueryParameter final String pPort,
944 | @QueryParameter final String pUser,
945 | @QueryParameter final String pPassword) {
946 | Jenkins.get().checkPermission(Jenkins.ADMINISTER);
947 |
948 | ProxyBlock proxy = null;
949 | if (useProxy != null && useProxy) {
950 | proxy = new ProxyBlock(useProxy, pHost, pPort, pUser, pPassword);
951 | }
952 |
953 | return validateConnection(acxServerURL, ncApiToken, proxy);
954 | }
955 |
956 | @SuppressWarnings("unused")
957 | public FormValidation doTestConnection(@QueryParameter final String credentialsId) {
958 |
959 | final String errorTemplate = "Error: %s";
960 |
961 | try {
962 |
963 | String descriptorUrl = getCurrentDescriptorByNameUrl();
964 |
965 | final StandardUsernamePasswordCredentials credential =
966 | AppCommon.findCredentialsById(credentialsId, descriptorUrl);
967 |
968 | if (credential == null) {
969 | return FormValidation.error(errorTemplate, "Credentials not found.");
970 | }
971 |
972 | String serverURL = credential.getUsername();
973 | Secret apiToken = credential.getPassword();
974 |
975 | ProxyBlock proxy = null;
976 | if (this.useProxy != null && this.useProxy) {
977 | proxy = new ProxyBlock(this.useProxy, this.pHost, this.pPort, this.pUser, this.pPassword);
978 | }
979 |
980 | return validateConnection(serverURL, apiToken, proxy);
981 |
982 | } catch (Exception e) {
983 | return FormValidation.error(e, errorTemplate, e.getMessage());
984 | }
985 | }
986 |
987 | @SuppressWarnings("unused")
988 | public FormValidation doCheckAcxServerURL(@QueryParameter String value) {
989 | if (value.length() == 0) {
990 | return FormValidation
991 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_missingApiURL());
992 | } else if (!AppCommon.isUrlValid(value)) {
993 | return FormValidation
994 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_invalidApiURL());
995 | }
996 |
997 | return FormValidation.ok();
998 | }
999 |
1000 | @POST
1001 | @SuppressWarnings("unused")
1002 | public FormValidation doCheckNcApiToken(@QueryParameter String value) {
1003 | if (value.length() == 0) {
1004 | return FormValidation
1005 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_missingApiToken());
1006 | }
1007 |
1008 | return FormValidation.ok();
1009 | }
1010 |
1011 | @SuppressWarnings("unused")
1012 | public FormValidation doCheckNcScanType(@QueryParameter String value) {
1013 | try {
1014 | ScanType.valueOf(value);
1015 | } catch (Exception ex) {
1016 | return FormValidation
1017 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_invalidScanType());
1018 | }
1019 |
1020 | return FormValidation.ok();
1021 | }
1022 |
1023 | @SuppressWarnings("unused")
1024 | public FormValidation doCheckNcWebsiteId(@QueryParameter String value) {
1025 |
1026 | if (!AppCommon.isGUIDValid(value)) {
1027 | return FormValidation
1028 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_invalidWebsiteId());
1029 | }
1030 |
1031 | return FormValidation.ok();
1032 | }
1033 |
1034 | @SuppressWarnings("unused")
1035 | public FormValidation doCheckNcProfileId(@QueryParameter String value,
1036 | @QueryParameter String ncScanType) {
1037 |
1038 | boolean isRequired;
1039 |
1040 | try {
1041 | ScanType type = ScanType.valueOf(ncScanType);
1042 | isRequired = type != ScanType.FullWithPrimaryProfile;
1043 | } catch (Exception ex) {
1044 | return FormValidation
1045 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_invalidProfileId());
1046 | }
1047 |
1048 | if (isRequired && !AppCommon.isGUIDValid(value)) {
1049 | return FormValidation
1050 | .error(Messages.ACXScanBuilder_DescriptorImpl_errors_invalidProfileId());
1051 | }
1052 |
1053 | return FormValidation.ok();
1054 | }
1055 |
1056 | @SuppressWarnings("unused")
1057 | public ListBoxModel doFillNcSeverityItems() throws IOException {
1058 | ListBoxModel items = new ListBoxModel();
1059 | items.add("Do not fail the build", "DoNotFail");
1060 | items.add("Critical", "Critical");
1061 | items.add("High or above", "Critical,High");
1062 | items.add("Medium or above", "Critical,High,Medium");
1063 | items.add("Low or above", "Critical,High,Medium,Low");
1064 | items.add("Best Practices or above", "Critical,High,Medium,Low,Best Practice");
1065 | return items;
1066 | }
1067 | }
1068 | }
1069 |
--------------------------------------------------------------------------------