diff --git a/Notices.txt b/Notices.txt new file mode 100644 index 00000000..2793e869 --- /dev/null +++ b/Notices.txt @@ -0,0 +1,278 @@ +----------------------- +Notices and Information +----------------------- +----------------------- +HCL AppScan SDK v1.1.11 +----------------------- + +====== +DETAIL +====== +Note: generic licenses are marked with (*) +Note: missing copyright references are marked with (*) +*************************************************************** +1. Library: appscan-sdk-appscan.sdk-1.1.10 Product: AppScan-SDK +*************************************************************** +-------- +PROJECTS +-------- +1.1.11 + + +======== +LICENSES +======== +Licensed under Apache 2.0 +License terms can be found at: https://github.com/HCL-TECH-SOFTWARE/appscan-sdk/blob/appscan.sdk-1.1.10/LICENSE + +License Text Under Appendix + + + +========== +COPYRIGHTS +========== +(*) The copyright for this library is unknown + +**************************************************** +2. Library: wink-json4j-1.4.jar Product: AppScan-SDK +**************************************************** +-------- +PROJECTS +-------- +1.1.11 + + +======== +LICENSES +======== +Licensed under Apache 2.0 +License terms can be found at: https://repo.maven.apache.org/maven2/org/apache/wink/wink-json4j/1.4/wink-json4j-1.4.pom + +License Text Under Appendix + + + +========== +COPYRIGHTS +========== +Copyrighted under Copyright The Apache Software Foundation +Copyright terms can be found at: null(*) Cannot be established + + + + +========================= +APPENDIX: License Details +========================= + +********** +Apache 2.0 +********** +License Text: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + diff --git a/README.md b/README.md index 9046f8c2..88a3c584 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # appscan-sdk -SDK for interacting with HCL AppScan on Cloud and HCL AppScan Enterprise ADAC Jobs +SDK for interacting with HCL AppScan on Cloud, HCL AppScan 360° and HCL AppScan Enterprise ADAC Jobs. # JavaDoc https://www.javadoc.io/doc/com.hcl/appscan.sdk/latest/index.html diff --git a/pom.xml b/pom.xml index c57af4ef..56e65d62 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.hcl appscan.sdk - 1.1.3-SNAPSHOT + 1.1.12-SNAPSHOT HCL AppScan on Cloud SDK SDK for interacting with HCL AppScan on Cloud @@ -22,6 +22,10 @@ + + UTF-8 + + @@ -45,10 +49,19 @@ maven-compiler-plugin 3.5.1 - 1.7 - 1.7 + 1.8 + 1.8 + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + central + + @@ -58,6 +71,16 @@ wink-json4j 1.4 + + org.apache.commons + commons-compress + 1.26.0 + + + commons-io + commons-io + 2.17.0 + @@ -123,12 +146,12 @@ - ossrh - https://oss.sonatype.org/content/repositories/snapshots + central + https://central.sonatype.com/repository/maven-snapshots - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + central + https://central.sonatype.com diff --git a/src/main/java/com/hcl/appscan/sdk/CoreConstants.java b/src/main/java/com/hcl/appscan/sdk/CoreConstants.java index c5722594..5235c22f 100644 --- a/src/main/java/com/hcl/appscan/sdk/CoreConstants.java +++ b/src/main/java/com/hcl/appscan/sdk/CoreConstants.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017, 2024. + * © Copyright HCL Technologies Ltd. 2017, 2026. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -10,13 +10,19 @@ public interface CoreConstants { String DEFAULT_SERVER = "https://cloud.appscan.com"; //$NON-NLS-1$ + //Technologies + String DYNAMIC_TECH = "DynamicAnalyzer"; //$NON-NLS-1$ + String SCA_TECH = "ScaAnalyzer"; //$NON-NLS-1$ + String STATIC_TECH = "StaticAnalyzer"; //$NON-NLS-1$ + String APP_ID = "AppId"; //$NON-NLS-1$ String EMAIL_NOTIFICATION = "EnableMailNotification"; //$NON-NLS-1$ - String PERSONAL_SCAN = "Personal"; //$NON-NLS-1$ + String PERSONAL_SCAN = "Personal"; //$NON-NLS-1$ String FILE_ID = "FileId"; //$NON-NLS-1$ String FILE_TO_UPLOAD = "fileToUpload"; //$NON-NLS-1$ String UPLOADED_FILE = "uploadedFile"; //$NON-NLS-1$ String ID = "Id"; //$NON-NLS-1$ + String SCAN_ID = "ScanId"; //$NON-NLS-1$ String KEY = "Key"; //$NON-NLS-1$ String LATEST_EXECUTION = "LatestExecution"; //$NON-NLS-1$ String LOCALE = "Locale"; //$NON-NLS-1$ @@ -27,18 +33,18 @@ public interface CoreConstants { String SCANNER_TYPE = "type"; //$NON-NLS-1$ String STATUS = "Status"; //$NON-NLS-1$ String TARGET = "target"; //$NON-NLS-1$ - String OPEN_SOURCE_ONLY = "openSourceOnly"; //$NON-NLS-1$ - String INCLUDE_SCA = "includeSCA"; //$NON-NLS-1$ + String OPEN_SOURCE_ONLY = "openSourceOnly"; //$NON-NLS-1$ + String INCLUDE_SCA = "includeSCA"; //$NON-NLS-1$ String VERSION_NUMBER = "VersionNumber"; //$NON-NLS-1$ String USER_MESSAGE = "UserMessage"; //$NON-NLS-1$ String IS_VALID = "IsValid"; //$NON-NLS-1$ - String SOURCE_CODE_ONLY = "sourceCodeOnly"; //$NON-NLS-1$ - String SOFTWARE_COMPOSITION_ANALYZER = "Software Composition Analyzer"; //$NON-NLS-1$ - String SCA = "Sca"; //$NON-NLS-1$ + String SOURCE_CODE_ONLY = "sourceCodeOnly"; //$NON-NLS-1$ + String SOFTWARE_COMPOSITION_ANALYZER= "Software Composition Analyzer"; //$NON-NLS-1$ + String SCA = "Sca"; //$NON-NLS-1$ - String CREATE_IRX = "createIRX"; //$NON-NLS-1$ - String UPLOAD_DIRECT = "uploadDirect"; //$NON-NLS-1$ - String BINDING_ID = "Bindingid"; //$NON-NLS-1$ + String CREATE_IRX = "createIRX"; //$NON-NLS-1$ + String UPLOAD_DIRECT = "uploadDirect"; //$NON-NLS-1$ + String BINDING_ID = "Bindingid"; //$NON-NLS-1$ String APPSCAN_OPTS = "APPSCAN_OPTS"; //$NON-NLS-1$ String BLUEMIX_SERVER = "BLUEMIX_SERVER"; //$NON-NLS-1$ String KEY_ID = "KeyId"; //$NON-NLS-1$ @@ -56,34 +62,37 @@ public interface CoreConstants { String CLIENT_TYPE = "ClientType"; //$NON-NLS-1$ String API_ENV = "/api/v2"; //$NON-NLS-1$ - String API_ENV_LATEST = "/api/v4"; //$NON-NLS-1$ + String API_ENV_LATEST = "/api/v4"; //$NON-NLS-1$ String API_BLUEMIX = "Bluemix"; //$NON-NLS-1$ String API_BLUEMIX_LOGIN = API_ENV + "/Account/BluemixLogin"; //$NON-NLS-1$ - String API_KEY_LOGIN = API_ENV_LATEST + "/Account/ApiKeyLogin"; //$NON-NLS-1$ + String API_KEY_LOGIN = API_ENV_LATEST + "/Account/ApiKeyLogin"; //$NON-NLS-1$ String API_LOGOUT = API_ENV + "/Account/Logout"; //$NON-NLS-1$ String API_APPS = API_ENV_LATEST + "/Apps"; //$NON-NLS-1$ - String API_PRESENCES = API_ENV_LATEST + "/Presences"; //$NON-NLS-1$ - String API_PRESENCES_ID = API_ENV_LATEST + "/Presences/%s"; //$NON-NLS-1$ - String API_PRESENCES_NEW_KEY = API_ENV_LATEST + "/Presences/%s/NewKey"; //$NON-NLS-1$ - String API_BASIC_DETAILS = API_ENV_LATEST + "/Scans"; //$NON-NLS-1$ - String API_SCANNER_DETAILS = API_ENV + "/Scans/&s/&s"; //$NON-NLS-1$ - String API_FILE_UPLOAD = API_ENV_LATEST + "/FileUpload"; //$NON-NLS-1$ + String API_PRESENCES = API_ENV_LATEST + "/Presences"; //$NON-NLS-1$ + String API_PRESENCES_ID = API_ENV_LATEST + "/Presences/%s"; //$NON-NLS-1$ + String API_PRESENCES_NEW_KEY = API_ENV_LATEST + "/Presences/%s/NewKey"; //$NON-NLS-1$ + String API_BASIC_DETAILS = API_ENV_LATEST + "/Scans"; //$NON-NLS-1$ + String API_SCANNER_DETAILS = API_ENV_LATEST + "/Scans/%s/%s"; //$NON-NLS-1$ + String API_FILE_UPLOAD = API_ENV_LATEST + "/FileUpload"; //$NON-NLS-1$ String API_SCAN = API_ENV + "/%s"; //$NON-NLS-1$ - String API_SCANNER = API_ENV_LATEST + "/Scans/%s"; //$NON-NLS-1$ + String API_SCANNER = API_ENV_LATEST + "/Scans/%s"; //$NON-NLS-1$ + String API_RESCAN = API_ENV_LATEST + "/Scans/%s/Executions"; //$NON-NLS-1$ String API_SCANS = API_ENV + "/Scans"; //$NON-NLS-1$ String API_NONCOMPLIANT_ISSUES = API_ENV + "/Scans/%s/NonCompliantIssues"; //$NON-NLS-1$ - String API_SCANS_REPORT = API_ENV_LATEST + "/Scans/%s/Report/%s"; //$NON-NLS-1$ - String API_REPORT_SELECTED_ISSUES = API_ENV_LATEST + "/Reports/Security/%s/%s"; //$NON-NLS-1$ - String API_DOWNLOAD_REPORT = API_ENV_LATEST + "/Reports/%s/Download"; //$NON-NLS-1$ - String API_SACLIENT_DOWNLOAD = API_ENV_LATEST + "/Tools/SAClientUtil?os=%s"; //$NON-NLS-1$ - String API_SACLIENT_VERSION = API_ENV_LATEST + "/Tools/SAClientUtil?os=%s&meta=%s"; //$NON-NLS-1$ + String API_SCANS_REPORT = API_ENV_LATEST + "/Scans/%s/Report/%s"; //$NON-NLS-1$ + String API_REPORT_SELECTED_ISSUES = API_ENV_LATEST + "/Reports/Security/%s/%s"; //$NON-NLS-1$ + String API_DOWNLOAD_REPORT = API_ENV_LATEST + "/Reports/%s/Download"; //$NON-NLS-1$ + String API_SACLIENT_DOWNLOAD = API_ENV_LATEST + "/Tools/SAClientUtilByType?toolType=%s"; //$NON-NLS-1$ + String API_SACLIENT_VERSION = API_ENV_LATEST + "/Tools/SAClientUtilByType?toolType=%s&meta=%s"; //$NON-NLS-1$ String API_KEY_PATH = "/api/ideclientuilogin"; //$NON-NLS-1$ - String API_REPORT_STATUS = API_ENV_LATEST + "/Reports"; //$NON-NLS-1$ - String API_ISSUES_COUNT = API_ENV_LATEST + "/Issues/%s/%s"; //$NON-NLS-1$ - String API_REGIONS = API_ENV_LATEST + "/Utils/Regions"; //$NON-NLS-1$ - String API_IS_VALID_URL = API_ENV_LATEST + "/Scans/IsValidUrl"; //$NON-NLS-1$ - String API_AUTHENTICATION = API_ENV_LATEST + "/Account/IsAuthenticated"; //$NON-NLS-1$ - String API_TENANT_INFO = API_ENV_LATEST + "/Account/TenantInfo"; //$NON-NLS-1$ + String API_REPORT_STATUS = API_ENV_LATEST + "/Reports"; //$NON-NLS-1$ + String API_ISSUES_COUNT = API_ENV_LATEST + "/Issues/%s/%s"; //$NON-NLS-1$ + String API_REGIONS = API_ENV_LATEST + "/Utils/Regions"; //$NON-NLS-1$ + String API_IS_VALID_URL = API_ENV_LATEST + "/Scans/IsValidUrl"; //$NON-NLS-1$ + String API_IS_VALID_DOMAIN = API_ENV_LATEST + "/Domains/Verify"; //$NON-NLS-1$ + String API_AUTHENTICATION = API_ENV_LATEST + "/Account/IsAuthenticated"; //$NON-NLS-1$ + String API_TENANT_INFO = API_ENV_LATEST + "/Account/TenantInfo"; //$NON-NLS-1$ + String API_EXECUTION_DETAILS = API_ENV_LATEST + "/Scans/%s/Executions"; //$NON-NLS-1$ String DEFAULT_RESULT_NAME = "asoc_results"; //$NON-NLS-1$ String SACLIENT_INSTALL_DIR = "SAClientInstall"; //$NON-NLS-1$ @@ -98,20 +107,27 @@ public interface CoreConstants { String PAUSING = "Pausing"; //$NON-NLS-1$ String PAUSED = "Paused"; //$NON-NLS-1$ String SUSPENDED = "Suspended"; //$NON-NLS-1$ - String UNKNOWN = "Unknown"; //$NON-NLS-1$ - String SERVER_URL = "serverURL"; //$NON-NLS-1$ - String ACCEPT_INVALID_CERTS = "acceptInvalidCerts"; //$NON-NLS-1$ - - String TOTAL_ISSUES = "NIssuesFound"; //$NON-NLS-1$ - String CRITICAL_ISSUES = "NCriticalIssues"; //$NON-NLS-1$ + String CANCELING = "Canceling"; //$NON-NLS-1$ + String UNKNOWN = "Unknown"; //$NON-NLS-1$ + String PARTIAL_SUCCESS = "PartialSuccess"; //$NON-NLS-1$ + String SERVER_URL = "serverURL"; //$NON-NLS-1$ + String ACCEPT_INVALID_CERTS = "acceptInvalidCerts"; //$NON-NLS-1$ + String FULL_SCAN = "Full Scan"; //$NON-NLS-1$ + String TEST_ONLY = "Test Only"; //$NON-NLS-1$ + String POSTMAN_COLLECTION = "Postman Collection"; //$NON-NLS-1$ + + String TOTAL_ISSUES = "NIssuesFound"; //$NON-NLS-1$ + String CRITICAL_ISSUES = "NCriticalIssues"; //$NON-NLS-1$ String HIGH_ISSUES = "NHighIssues"; //$NON-NLS-1$ String MEDIUM_ISSUES = "NMediumIssues"; //$NON-NLS-1$ String LOW_ISSUES = "NLowIssues"; //$NON-NLS-1$ String INFO_ISSUES = "NInfoIssues"; //$NON-NLS-1$ - String ITEMS = "Items"; //$NON-NLS-1$ + String ITEMS = "Items"; //$NON-NLS-1$ String CREATE_SCAN_SUCCESS = "message.created.scan"; //$NON-NLS-1$ String SCAN_OVERVIEW = "message.scan.overview"; //$NON-NLS-1$ + String RESCAN_SUCCESS = "message.rescan"; //$NON-NLS-1$ + String RESCAN_OVERVIEW = "message.rescan.overview"; //$NON-NLS-1$ String DOWNLOADING_CLIENT = "message.downloading.client"; //$NON-NLS-1$ String EXECUTING_SCAN = "message.running.scan"; //$NON-NLS-1$ String UPLOADING_FILE = "message.uploading.file"; //$NON-NLS-1$ @@ -120,11 +136,12 @@ public interface CoreConstants { String REGIONS = "Regions"; //$NON-NLS-1$ String DEFAULT_REGION = "DefaultRegion"; //$NON-NLS-1$ String URL = "Url"; //$NON-NLS-1$ - + String STP = "STP"; //$NON-NLS-1$ + String ERROR_AUTHENTICATING = "error.authenticating"; //$NON-NLS-1$ String ERROR_DOWNLOADING_CLIENT = "error.download.client"; //$NON-NLS-1$ String ERROR_GETTING_DETAILS = "error.getting.details"; //$NON-NLS-1$ - String ERROR_GETTING_DETAILS_SCAN_ID = "error.getting.details.scan.id"; //$NON-NLS-1$ + String ERROR_GETTING_DETAILS_SCAN_ID= "error.getting.details.scan.id"; //$NON-NLS-1$ String ERROR_GETTING_RESULT = "error.getting.result"; //$NON-NLS-1$ String ERROR_GENERATING_REPORT = "error.generating.report"; //$NON-NLS-1$ String ERROR_INVALID_APP = "error.invalid.app"; //$NON-NLS-1$ @@ -135,49 +152,52 @@ public interface CoreConstants { String ERROR_SUBMITTING_SCAN = "error.submit.scan"; //$NON-NLS-1$ String ERROR_UPLOADING_FILE = "error.upload.file"; //$NON-NLS-1$ String ERROR_GETTING_INFO = "error.getting.info"; //$NON-NLS-1$ - String ERROR_URL_VALIDATION = "error.url.validation"; //$NON-NLS-1$ - String FORMAT_PARAMS = "FormatParams"; //$NON-NLS-1$ - + String ERROR_URL_VALIDATION = "error.url.validation"; //$NON-NLS-1$ + String FORMAT_PARAMS = "FormatParams"; //$NON-NLS-1$ String ERROR_GETTING_SCANLOG = "error.getting.scanlog"; //$NON-NLS-1$ - + String ERROR_CANCEL_RESCAN = "error.cancel.rescan"; //$NON-NLS-1$ // ASE Status Messages String CREATING_JOB = "message.creating.job"; //$NON-NLS-1$ String CREATE_JOB_SUCCESS = "message.created.job"; //$NON-NLS-1$ String ERROR_CREATE_JOB = "error.create.job"; //$NON-NLS-1$ + String UPDATE_JOB = "message.update.job"; //$NON-NLS-1$ String ERROR_UPDATE_JOB = "error.update.job"; //$NON-NLS-1$ String EXECUTING_JOB = "message.running.job"; //$NON-NLS-1$ String EXECUTE_JOB_SUCCESS = "message.executed.job"; //$NON-NLS-1$ String ERROR_EXECUTE_JOB = "error.execute.job"; //$NON-NLS-1$ String RESULTS_UNAVAILABLE = "message.results.unavailable"; //$NON-NLS-1$ String ERROR_INVALID_DETAILS = "error.invalid.details"; //$NON-NLS-1$ + String UPDATE_POSTMAN_COLLECTION_SUCCESS = "update.postman.collection.success"; //$NON-NLS-1$ + String ERROR_FILE_NOT_FOUND = "error.file.not.found"; //$NON-NLS-1$ // ASE APIs - String ASE_API = "/api"; //$NON-NLS-1$ - String ASE_APPS = ASE_API + "/applications"; //$NON-NLS-1$ - String ASE_FOLDERS = ASE_API + "/folders"; //$NON-NLS-1$ - String ASE_TEST_POLICIES = ASE_API + "/testPolicies"; //$NON-NLS-1$ - String ASE_AGENT_SERVER = ASE_API + "/agentServer"; //$NON-NLS-1$ - String ASE_UPDSCANT = ASE_API + "/jobs/%s/dastconfig/updatescant"; //$NON-NLS-1$ - String ASE_UPDTRAFFIC = ASE_API + "/jobs/%s/dastconfig/updatetraffic/%s"; //$NON-NLS-1$ - String ASE_UPDTAGENT = ASE_API + "/jobs/%s/designateAgentServer/%s"; //$NON-NLS-1$ - String ASE_SCAN_TYPE = ASE_API + "/jobs/scantype"; //$NON-NLS-1$ - String ASE_LOGIN_API = ASE_API + "/keylogin/apikeylogin"; //$NON-NLS-1$ - String ASE_KEY_ID = "keyId"; //$NON-NLS-1$ - String ASE_KEY_SECRET = "keySecret"; //$NON-NLS-1$ - String ASE_NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ - String ASE_ID_ATTRIBUTE = "id"; //$NON-NLS-1$ - String ASE_CREATEJOB_TEMPLATE_ID = ASE_API + "/jobs/%s/dastconfig/createjob"; //$NON-NLS-1$ - String ASE_GET_JOB = ASE_API + "/jobs/%s"; //$NON-NLS-1$ - String ASE_RUN_JOB_ACTION = ASE_API +"/jobs/%s/actions"; //$NON-NLS-1$ - String ASE_REPORTPACK = ASE_API + "/folderitems/%s/reportPack"; //$NON-NLS-1$ - String ASE_REPORTS = ASE_API + "/folderitems/%s/reports"; //$NON-NLS-1$ - String ASE_GET_FOLDERITEMS = ASE_API + "/folderitems/%s"; //$NON-NLS-1$ - String ASE_UPLOADED_FILE = "uploadedfile"; //$NON-NLS-1$ - String ASE_CURRENTUSER_V2 = ASE_API + "/currentuser_v2"; //$NON-NLS-1$ - String ASE_GET_FOLDER_ITEMS_STATISTICS = ASE_API + "/folderitems/%s/statistics"; //$NON-NLS-1$ - - String UNAUTHORIZED_ACTION = "UNAUTHORIZED_ACTION"; //$NON-NLS-1$ - - String API_SCANS_SCANLOGS = API_ENV_LATEST + "/Scans/ScanLogs/%s"; + String ASE_API = "/api"; //$NON-NLS-1$ + String ASE_APPS = ASE_API + "/applications"; //$NON-NLS-1$ + String ASE_FOLDERS = ASE_API + "/folders"; //$NON-NLS-1$ + String ASE_TEST_POLICIES = ASE_API + "/testPolicies"; //$NON-NLS-1$ + String ASE_AGENT_SERVER = ASE_API + "/agentServer"; //$NON-NLS-1$ + String ASE_UPDSCANT = ASE_API + "/jobs/%s/dastconfig/updatescant"; //$NON-NLS-1$ + String ASE_UPDTRAFFIC = ASE_API + "/jobs/%s/dastconfig/updatetraffic/%s"; //$NON-NLS-1$ + String ASE_UPDTAGENT = ASE_API + "/jobs/%s/designateAgentServer/%s"; //$NON-NLS-1$ + String ASE_SCAN_TYPE = ASE_API + "/jobs/scantype"; //$NON-NLS-1$ + String ASE_LOGIN_API = ASE_API + "/keylogin/apikeylogin"; //$NON-NLS-1$ + String ASE_KEY_ID = "keyId"; //$NON-NLS-1$ + String ASE_KEY_SECRET = "keySecret"; //$NON-NLS-1$ + String ASE_NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ + String ASE_ID_ATTRIBUTE = "id"; //$NON-NLS-1$ + String ASE_CREATEJOB_TEMPLATE_ID = ASE_API + "/jobs/%s/dastconfig/createjob"; //$NON-NLS-1$ + String ASE_GET_JOB = ASE_API + "/jobs/%s"; //$NON-NLS-1$ + String ASE_RUN_JOB_ACTION = ASE_API +"/jobs/%s/actions"; //$NON-NLS-1$ + String ASE_REPORTPACK = ASE_API + "/folderitems/%s/reportPack"; //$NON-NLS-1$ + String ASE_REPORTS = ASE_API + "/folderitems/%s/reports"; //$NON-NLS-1$ + String ASE_GET_FOLDERITEMS = ASE_API + "/folderitems/%s"; //$NON-NLS-1$ + String ASE_UPLOADED_FILE = "uploadedfile"; //$NON-NLS-1$ + String ASE_CURRENTUSER_V2 = ASE_API + "/currentuser_v2"; //$NON-NLS-1$ + String ASE_GET_FOLDER_ITEMS_STATISTICS = ASE_API + "/folderitems/%s/statistics"; //$NON-NLS-1$ + String ASE_POSTMAN_COLLECTION = ASE_API + "/jobs/%s/dastconfig/postman/create"; //$NON-NLS-1$ + + String UNAUTHORIZED_ACTION = "UNAUTHORIZED_ACTION"; //$NON-NLS-1$ + + String API_SCANS_SCANLOGS = API_ENV_LATEST + "/Scans/ScanLogs/%s"; //$NON-NLS-1$ } diff --git a/src/main/java/com/hcl/appscan/sdk/http/HttpClient.java b/src/main/java/com/hcl/appscan/sdk/http/HttpClient.java index 2caf1399..7352a17c 100644 --- a/src/main/java/com/hcl/appscan/sdk/http/HttpClient.java +++ b/src/main/java/com/hcl/appscan/sdk/http/HttpClient.java @@ -18,7 +18,6 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -158,6 +157,35 @@ public HttpResponse put(String url, return makeRequest(Method.PUT, url, headerProperties, body); } + /** + * Submit a put request. + * + * @param url The URL string. + * @param headerProperties An optional Map of header properties. + * @param parameters An optional Map of properties. + * @return The response as a byte array. + * @throws IOException If an error occurs. + */ + public HttpResponse put(String url, Map headerProperties, Map parameters) + throws IOException, JSONException { + JSONObject objectMap = new JSONObject(); + for (String key : parameters.keySet()) { + if (parameters.get(key) != null){ + String value = parameters.get(key); + if (value.equalsIgnoreCase("true")) { + objectMap.put(key, true); + } else if (value.equalsIgnoreCase("false")) { + objectMap.put(key, false); + } else { + // If the string is not "true" or "false," keep it as is + objectMap.put(key, value); + } + } + } + String body = objectMap.toString(); + return put(url, headerProperties, body); + } + /** * Submit a delete request. * diff --git a/src/main/java/com/hcl/appscan/sdk/messages.properties b/src/main/java/com/hcl/appscan/sdk/messages.properties index 4564dc39..b8d59c64 100644 --- a/src/main/java/com/hcl/appscan/sdk/messages.properties +++ b/src/main/java/com/hcl/appscan/sdk/messages.properties @@ -8,8 +8,10 @@ transfer.progress={0}% transferred -message.created.scan=Successfully submitted {0} scan for analysis. Scan ID: {1} -message.scan.overview={0} scan overview: {1} +message.created.scan=Successfully submitted {0} scan for analysis. Scan ID: +message.scan.overview={0} scan overview: +message.rescan= Successfully submitted rescan for analysis. Execution ID: +message.rescan.overview= Rescan overview: message.running.scan=Creating and executing {0} scan... message.uploading.file=Uploading {0} to the analysis service... message.done=Done. @@ -54,6 +56,8 @@ error.login.type.deprectated=The specified login type is deprecated. Please use error.getting.info=An error occurred getting information for {0} with id {1}. error.getting.scanlog=An error occurred retrieving the scan log. error.url.validation = An error occurred while validating the Starting URL: {0}. +message.update.job = Updated the scan job parameters. +error.cancel.rescan = Rescan ID {0} not found. It may have been canceled or deleted. Verify the scan status on the AppScan server and retry. #Presence error.getting.presence.details=An error occurred retrieving details for Presence with id {0}. @@ -74,3 +78,5 @@ message.running.job=Executing job... message.executed.job=Job executed successfully. error.execute.job=An error occurred executing the job. {0} error.invalid.details=The job details contain missing or invalid parameters. +update.postman.collection.success=Successfully updated Postman collection for jobId: {0}. +error.file.not.found=File {0} not found. diff --git a/src/main/java/com/hcl/appscan/sdk/results/ASEResultsProvider.java b/src/main/java/com/hcl/appscan/sdk/results/ASEResultsProvider.java index e84e4398..0bdfbc0e 100644 --- a/src/main/java/com/hcl/appscan/sdk/results/ASEResultsProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/results/ASEResultsProvider.java @@ -1,5 +1,5 @@ /** - * © Copyright HCL Technologies Ltd. 2019, 2020, 2024. + * © Copyright HCL Technologies Ltd. 2019, 2025. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -185,6 +185,8 @@ private void loadResults() { m_message = ""; } else if (RUNNING.equalsIgnoreCase(m_status)) { m_message = ""; + } else if (CANCELING.equalsIgnoreCase(m_status)) { + m_status = FAILED; } else if (m_status.startsWith(SUSPENDED)) { // In case of Scan Failure ASE returns Suspended (With Reason) in Status this.m_message = m_status; String description = ""; @@ -273,8 +275,8 @@ private void checkResults() { private String getReportPackId(String scanId) { return String.valueOf(Integer.parseInt(scanId)+1); - // please uncomment the below code when you figure out how to parse the reponse. - // currently the reponse is returned as array which makes no sense. + // please uncomment the below code when you figure out how to parse the response. + // currently the response is returned as array which makes no sense. /*IAuthenticationProvider authProvider = m_scanProvider.getAuthenticationProvider(); if(authProvider.isTokenExpired()) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_LOGIN_EXPIRED))); @@ -401,4 +403,9 @@ private String getStatisticsStatus(String jobId) { } return null; } + + @Override + public void getScanLogFile(File file) { + // Not applicable for ASE Results Provider + } } diff --git a/src/main/java/com/hcl/appscan/sdk/results/CloudCombinedResultsProvider.java b/src/main/java/com/hcl/appscan/sdk/results/CloudCombinedResultsProvider.java index 1c946f5e..56c48d75 100644 --- a/src/main/java/com/hcl/appscan/sdk/results/CloudCombinedResultsProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/results/CloudCombinedResultsProvider.java @@ -1,5 +1,5 @@ /** - * © Copyright HCL Technologies Ltd. 2024. + * © Copyright HCL Technologies Ltd. 2024, 2025. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -11,6 +11,7 @@ import com.hcl.appscan.sdk.CoreConstants; import com.hcl.appscan.sdk.logging.IProgress; +import com.hcl.appscan.sdk.utils.ServiceUtil; public class CloudCombinedResultsProvider implements IResultsProvider, Serializable { @@ -18,6 +19,8 @@ public class CloudCombinedResultsProvider implements IResultsProvider, Serializa private IResultsProvider m_resultsProvider1; private IResultsProvider m_resultsProvider2; + private String m_status1; + private String m_status2; private String m_reportFormat = DEFAULT_REPORT_FORMAT; public CloudCombinedResultsProvider(IResultsProvider resultsProvider1, IResultsProvider resultsProvider2) { @@ -27,22 +30,37 @@ public CloudCombinedResultsProvider(IResultsProvider resultsProvider1, IResultsP @Override public boolean hasResults() { - return m_resultsProvider1.hasResults() || m_resultsProvider2.hasResults(); + return getStatus().equals(CoreConstants.PARTIAL_SUCCESS) ? (m_resultsProvider1.hasResults() || m_resultsProvider2.hasResults()) : (m_resultsProvider1.hasResults() && m_resultsProvider2.hasResults()); + } + + public IResultsProvider getResultsProvider1() { + return m_resultsProvider1; + } + + public IResultsProvider getResultsProvider2() { + return m_resultsProvider2; } @Override public String getStatus() { + // Default to RUNNING String combinedStatus = CoreConstants.RUNNING; - String status1 = m_resultsProvider1.getStatus(); - String status2 = m_resultsProvider2.getStatus(); - - if(status1.equalsIgnoreCase(CoreConstants.FAILED) || status2.equalsIgnoreCase(CoreConstants.FAILED)) { - combinedStatus = CoreConstants.FAILED; + + // Fetch individual status + if (m_status1 == null || m_status1.equalsIgnoreCase(CoreConstants.RUNNING)) { + m_status1 = m_resultsProvider1.getStatus(); } - else if(status1.equalsIgnoreCase(CoreConstants.READY) && status2.equalsIgnoreCase(CoreConstants.READY)) { - combinedStatus = CoreConstants.READY; + if (m_status2 == null || m_status2.equalsIgnoreCase(CoreConstants.RUNNING)) { + m_status2 = m_resultsProvider2.getStatus(); + } + + // Handle different status combinations + if (m_status1.equals(m_status2)) { + combinedStatus = m_status1; + } else if ((CoreConstants.READY.equalsIgnoreCase(m_status1) || CoreConstants.READY.equalsIgnoreCase(m_status2)) && + (CoreConstants.FAILED.equalsIgnoreCase(m_status1) || CoreConstants.FAILED.equalsIgnoreCase(m_status2))) { + combinedStatus = CoreConstants.PARTIAL_SUCCESS; } - return combinedStatus; } @@ -89,10 +107,11 @@ public String getType() { @Override public void getResultsFile(File destination, String format) { //Append the technology type to the end of the file name. - String name = destination.getName(); + String name1 = ServiceUtil.scanTypeShortForm(m_resultsProvider1.getType()).toUpperCase()+"_"+destination.getName(); + String name2 = ServiceUtil.scanTypeShortForm(m_resultsProvider2.getType()).toUpperCase()+"_"+destination.getName(); File directory = destination.getParentFile(); - m_resultsProvider1.getResultsFile(new File(directory, name), format); - m_resultsProvider2.getResultsFile(new File(directory, name), format); + m_resultsProvider1.getResultsFile(new File(directory, name1), format); + m_resultsProvider2.getResultsFile(new File(directory, name2), format); } @Override @@ -121,4 +140,11 @@ public void setProgress(IProgress progress) { m_resultsProvider1.setProgress(progress); m_resultsProvider2.setProgress(progress); } + + @Override + public void getScanLogFile(File file) { + if (m_status1 != null && m_status1.equalsIgnoreCase(CoreConstants.READY)) { + m_resultsProvider1.getScanLogFile(file); + } + } } diff --git a/src/main/java/com/hcl/appscan/sdk/results/CloudResultsProvider.java b/src/main/java/com/hcl/appscan/sdk/results/CloudResultsProvider.java index ae77fb80..a0f19c48 100644 --- a/src/main/java/com/hcl/appscan/sdk/results/CloudResultsProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/results/CloudResultsProvider.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017, 2024. + * © Copyright HCL Technologies Ltd. 2017, 2025. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -47,10 +47,16 @@ public class CloudResultsProvider implements IResultsProvider, Serializable, Cor protected int m_mediumFindings; protected int m_lowFindings; protected int m_infoFindings; + protected String m_executionId; public CloudResultsProvider(String scanId, String type, IScanServiceProvider provider, IProgress progress) { + this(scanId, null, type, provider, progress); + } + + public CloudResultsProvider(String scanId, String executionId, String type, IScanServiceProvider provider, IProgress progress) { m_type = type; m_scanId = scanId; + m_executionId = executionId; m_hasResults = false; m_scanProvider = provider; m_progress = progress; @@ -241,24 +247,28 @@ protected String getReportStatus(String reportId) throws IOException, JSONExcept } - public void getScanLogFile(File file , String executionId) { - + public void getScanLogFile(File file , String scanId) { if(file != null && !file.exists()) { try { - getScanLog(executionId, file); + getScanLog(scanId, file); } catch (IOException | JSONException e) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_SCANLOG)), e); } } } - private void getScanLog(String executionId, File destination) throws IOException, JSONException { + @Override + public void getScanLogFile(File file) { + getScanLogFile(file, m_scanId); + } + + private void getScanLog(String scanId, File destination) throws IOException, JSONException { IAuthenticationProvider authProvider = m_scanProvider.getAuthenticationProvider(); if(authProvider.isTokenExpired()) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_LOGIN_EXPIRED))); return; } - String request_url = authProvider.getServer() + String.format(API_SCANS_SCANLOGS, executionId); + String request_url = authProvider.getServer() + String.format(API_SCANS_SCANLOGS, scanId); Map request_headers = authProvider.getAuthorizationHeader(true); request_headers.put(CONTENT_LENGTH, "0"); //$NON-NLS-1$ diff --git a/src/main/java/com/hcl/appscan/sdk/results/IResultsProvider.java b/src/main/java/com/hcl/appscan/sdk/results/IResultsProvider.java index 83c81767..bab389df 100644 --- a/src/main/java/com/hcl/appscan/sdk/results/IResultsProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/results/IResultsProvider.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017, 2024. + * © Copyright HCL Technologies Ltd. 2017, 2025. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -109,4 +109,10 @@ public interface IResultsProvider { * @param progress The IProgress. */ public void setProgress(IProgress progress); + + /** + * Gets the results in a file. + * @param destination The File to store the scan logs. + */ + public void getScanLogFile(File destination); } diff --git a/src/main/java/com/hcl/appscan/sdk/results/NonCompliantIssuesResultProvider.java b/src/main/java/com/hcl/appscan/sdk/results/NonCompliantIssuesResultProvider.java index aef88265..bd31e4ae 100644 --- a/src/main/java/com/hcl/appscan/sdk/results/NonCompliantIssuesResultProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/results/NonCompliantIssuesResultProvider.java @@ -1,5 +1,5 @@ /** - * © Copyright HCL Technologies Ltd. 2018, 2024. + * © Copyright HCL Technologies Ltd. 2018, 2025. */ package com.hcl.appscan.sdk.results; @@ -33,6 +33,10 @@ public NonCompliantIssuesResultProvider(String scanId, String type, IScanService super(scanId, type, provider, progress); } + public NonCompliantIssuesResultProvider(String scanId, String executionId, String type, IScanServiceProvider provider, IProgress progress) { + super(scanId, executionId, type, provider, progress); + } + @Override protected void loadResults() { try { @@ -52,8 +56,19 @@ protected void loadResults() { return; } + if (m_executionId != null && !m_executionId.isEmpty()) { + String executionId = obj.getString(ID); + if (executionId.equals(m_executionId)) { + m_status = obj.getString(STATUS); + } else { + m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(ERROR_CANCEL_RESCAN, m_executionId))); + m_status = FAILED; + } + } else { + m_status = obj.getString(STATUS); + m_executionId = obj.getString(ID); + } - m_status = obj.getString(STATUS); if (FAILED.equalsIgnoreCase(m_status) && obj.has(USER_MESSAGE)) { m_progress.setStatus(new Message(Message.ERROR, obj.getString(USER_MESSAGE))); m_message = obj.getString(USER_MESSAGE); @@ -61,38 +76,47 @@ protected void loadResults() { m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId))); m_message = Messages.getMessage(SUSPEND_JOB_BYUSER, "Scan Id: " + m_scanId); } else if (m_status != null && !(m_status.equalsIgnoreCase(INQUEUE) || m_status.equalsIgnoreCase(RUNNING) || m_status.equalsIgnoreCase(PAUSING))) { - JSONArray array = m_scanProvider.getNonCompliantIssues(m_scanId); + JSONArray array; + if(m_executionId != null && !m_executionId.isEmpty()) { + array = m_scanProvider.getNonCompliantIssuesUsingExecutionId(m_executionId); + } else { + array = m_scanProvider.getNonCompliantIssues(m_scanId); + } m_totalFindings = 0; - for (int i = 0; i < array.length(); i++) { - JSONObject jobj = array.getJSONObject(i); - String sev = jobj.getString("Severity"); - int count = jobj.getInt("N"); - - switch (sev.toLowerCase()) { - case "critical": - m_criticalFindings += count; - m_totalFindings += count; - break; - case "high": - m_highFindings += count; - m_totalFindings += count; - break; - case "medium": - m_mediumFindings += count; - m_totalFindings += count; - break; - case "low": - m_lowFindings += count; - m_totalFindings += count; - break; - case "informational": - m_infoFindings += count; - m_totalFindings += count; - break; - default: - m_totalFindings += count; - break; + if(array == null) { + m_status = FAILED; + } else { + for (int i = 0; i < array.length(); i++) { + JSONObject jobj = array.getJSONObject(i); + String sev = jobj.getString("Severity"); + int count = jobj.getInt("N"); + + switch (sev.toLowerCase()) { + case "critical": + m_criticalFindings += count; + m_totalFindings += count; + break; + case "high": + m_highFindings += count; + m_totalFindings += count; + break; + case "medium": + m_mediumFindings += count; + m_totalFindings += count; + break; + case "low": + m_lowFindings += count; + m_totalFindings += count; + break; + case "informational": + m_infoFindings += count; + m_totalFindings += count; + break; + default: + m_totalFindings += count; + break; + } } } setHasResult(true); @@ -179,7 +203,12 @@ private String createNonCompliantIssuesReport(String scanId, String format) thro return null; } - String request_url = authProvider.getServer() + String.format(API_REPORT_SELECTED_ISSUES, SCOPE, scanId); + String request_url; + if(m_executionId != null && !m_executionId.isEmpty()) { + request_url = authProvider.getServer() + String.format(API_REPORT_SELECTED_ISSUES, "ScanExecution", m_executionId); + } else { + request_url = authProvider.getServer() + String.format(API_REPORT_SELECTED_ISSUES, SCOPE, scanId); + } Map request_headers = authProvider.getAuthorizationHeader(true); request_headers.put("Content-Type", "application/json; charset=UTF-8"); request_headers.put("Accept", "application/json"); @@ -233,4 +262,8 @@ private String getScanName() { } } + + public void getScanLogs(File file) { + getScanLogFile(file); + } } diff --git a/src/main/java/com/hcl/appscan/sdk/scan/ASEScanServiceProvider.java b/src/main/java/com/hcl/appscan/sdk/scan/ASEScanServiceProvider.java index b8c10897..93830111 100644 --- a/src/main/java/com/hcl/appscan/sdk/scan/ASEScanServiceProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/scan/ASEScanServiceProvider.java @@ -1,5 +1,5 @@ /** - * © Copyright HCL Technologies Ltd. 2019,2020. + * © Copyright HCL Technologies Ltd. 2019,2020, 2025. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -24,6 +24,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.HttpsURLConnection; + import org.apache.wink.json4j.JSONArray; import org.apache.wink.json4j.JSONException; import org.apache.wink.json4j.JSONObject; @@ -56,7 +57,7 @@ private String createJob(Map params) { if(loginExpired()) return null; - Map createJobParams = getcreateJobParams(params); + Map createJobParams = getCreateJobParams(params); m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATING_JOB))); // TODO : correct it . @@ -64,44 +65,54 @@ private String createJob(Map params) { createJobParams.remove("templateId"); String request_url = m_authProvider.getServer() + String.format(ASE_CREATEJOB_TEMPLATE_ID, templateId); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); try { HttpResponse response = client.postForm(request_url, request_headers, createJobParams); int status = response.getResponseCode(); - - // Handle scenarios of invalid input parameters during job creation. - // Currently ASE APIs do not return a valid response for invalid inputs - // hence, making the check here for better error handling - if (status == HttpsURLConnection.HTTP_BAD_REQUEST - || status == HttpsURLConnection.HTTP_NOT_FOUND) { - m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage( - ERROR_CREATE_JOB, Messages.getMessage(ERROR_INVALID_DETAILS)))); - return null; + JSONObject json = null; + + // Safely parse JSON response if available + try { + Object responseBody = response.getResponseBodyAsJSON(); + if (responseBody instanceof JSONObject) { + json = (JSONObject) responseBody; + } + } catch (IOException | JSONException ignored) { + // Ignore JSON parsing issues, handled later } - JSONObject json = (JSONObject) response.getResponseBodyAsJSON(); - - if (status == HttpsURLConnection.HTTP_CREATED) { + // Success case: 201 + valid ID + if (status == HttpsURLConnection.HTTP_CREATED && json != null && json.has(ASE_ID_ATTRIBUTE)) { + String jobId = json.getString(ASE_ID_ATTRIBUTE); m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_JOB_SUCCESS))); - return json.getString(ASE_ID_ATTRIBUTE); + return jobId; } - else if (json != null && json.has(MESSAGE)) - m_progress.setStatus(new Message(Message.ERROR, json.getString(MESSAGE))); - else - m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_CREATE_JOB, status))); - } catch(IOException | JSONException e) { + + // Invalid input parameters + else if (status == HttpsURLConnection.HTTP_BAD_REQUEST || status == HttpsURLConnection.HTTP_NOT_FOUND) { + String errorMessage = ""; + if (json != null) { + if (json.has("errorMessage")) { + errorMessage = json.optString("errorMessage"); + } else if (json.has(MESSAGE)) { + errorMessage = json.optString(MESSAGE); + } + } + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_CREATE_JOB, errorMessage.isEmpty() ? Messages.getMessage(ERROR_INVALID_DETAILS) : errorMessage))); + } else { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_CREATE_JOB, Messages.getMessage(ERROR_INVALID_DETAILS)))); + } + + } catch (IOException | JSONException e) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_CREATE_JOB, e.getLocalizedMessage()))); } return null; } - private Map getcreateJobParams(Map properties) { + private Map getCreateJobParams(Map properties) { Map apiParams= new HashMap<>(); apiParams.put("testPolicyId", properties.get("testPolicyId")); apiParams.put("folderId",properties.get("folder")); @@ -114,70 +125,73 @@ private Map getcreateJobParams(Map properties) { } private String updateJob(Map params, String jobId) { + String scanTypeValue = params.getOrDefault("scanType", ""); - // Starting URL - if(!params.get("startingURL").isEmpty() && !updatescantJob(getUpdatescantJobParams("StartingUrl", params.get("startingURL"), "false"),jobId)) { - return null; - } + if (!handleStartingURL(params, jobId, scanTypeValue)) return null; + if (!handleAgentServer(params, jobId)) return null; + if (!handleLoginManagement(params, jobId)) return null; + if (!handleExploreData(params, jobId)) return null; + if (!handleScanType(params, jobId, scanTypeValue)) return null; + if (!handleTestOptimization(params, jobId)) return null; + if (!handlePostmanCollection(params, jobId, scanTypeValue)) return null; - // Agent Server - if(!params.get("agentServer").isEmpty() && !updateAgentServer(params, jobId)) { - return null; - } + return jobId; + } - // Login Management - if (!params.get("loginType").isEmpty()) { - - String loginType = params.get("loginType"); - if(!updatescantJob(getUpdatescantJobParams("LoginMethod", loginType, "false"),jobId)) { - return null; - } - - if (loginType.equals("Automatic")) - { - boolean status = updatescantJob(getUpdatescantJobParams("LoginUsername", params.get("userName"), "false"),jobId ); - if (status) { - status = updatescantJob(getUpdatescantJobParams("LoginPassword", params.get("password"), "true"),jobId); - } - if(!status) - return null; - } - - if (loginType.equals("Manual") && !updateTrafficJob(getFile(params.get("trafficFile")),jobId,"login")) { - return null; - } - } + private boolean handleStartingURL(Map params, String jobId, String scanType) { + return scanType.equals(POSTMAN_COLLECTION) || params.get("startingURL").isEmpty() || + updatescantJob(getUpdatescantJobParams("StartingUrl", params.get("startingURL"), "false"), jobId); + } - // Explore Data - if(!params.get("exploreData").isEmpty() && !updateTrafficJob(getFile(params.get("exploreData")),jobId,"add")) { - return null; - } + private boolean handleAgentServer(Map params, String jobId) { + return params.get("agentServer").isEmpty() || updateAgentServer(params, jobId); + } + + private boolean handleLoginManagement(Map params, String jobId) { + String loginType = params.getOrDefault("loginType", ""); + if (loginType.isEmpty()) return true; - // Scan Type - if(!params.get("scanType").isEmpty() && !scanTypeJob(params, jobId)) { - return null; + if (!updatescantJob(getUpdatescantJobParams("LoginMethod", loginType, "false"), jobId)) return false; + + if (loginType.equals("Automatic")) { + if (!updatescantJob(getUpdatescantJobParams("LoginUsername", params.get("userName"), "false"), jobId)) return false; + if (!updatescantJob(getUpdatescantJobParams("LoginPassword", params.get("password"), "true"), jobId)) return false; } - // Test Optimization - if(!params.get("testOptimization").isEmpty() && - !updatescantJob(getUpdatescantJobParams("TestOptimization", - params.get("testOptimization"), "false"), jobId)) { - return null; + if (loginType.equals("Manual")) { + File trafficFile = getFile(params.get("trafficFile")); + if (trafficFile != null && !updateTrafficJob(trafficFile, jobId, "login")) return false; } - return jobId; + return true; + } + + private boolean handleExploreData(Map params, String jobId) { + String exploreData = params.getOrDefault("exploreData", ""); + return exploreData.isEmpty() || updateTrafficJob(getFile(exploreData), jobId, "add"); + } + + private boolean handleScanType(Map params, String jobId, String scanType) { + return scanType.isEmpty() || scanType.equals(POSTMAN_COLLECTION) || updateScanTypeJob(params, jobId); + } + + private boolean handleTestOptimization(Map params, String jobId) { + String testOpt = params.getOrDefault("testOptimization", ""); + return testOpt.isEmpty() || updatescantJob(getUpdatescantJobParams("TestOptimization", testOpt, "false"), jobId); } - - private Boolean updatescantJob(Map params, String jobId) { + + private boolean handlePostmanCollection(Map params, String jobId, String scanType) { + return !scanType.equals(POSTMAN_COLLECTION) || createPostmanCollectionJob(params, jobId); + } + + + private boolean updatescantJob(Map params, String jobId) { if(loginExpired()) return false; String request_url = m_authProvider.getServer() + String.format(ASE_UPDSCANT, jobId); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); @@ -194,16 +208,13 @@ private Boolean updatescantJob(Map params, String jobId) { return true; } - private Boolean scanTypeJob (Map params, String jobId) { + private boolean updateScanTypeJob (Map params, String jobId) { if(loginExpired()) return false; - String request_url = m_authProvider.getServer() + String.format(ASE_SCAN_TYPE) + "?scanTypeId=" + params.get("scanType") + "&jobId="+ jobId; - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + String request_url = m_authProvider.getServer() + String.format(ASE_SCAN_TYPE) + "?scanTypeId=" + ASEScanType.scanTypeCode(params.get("scanType")) + "&jobId="+ jobId; + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); @@ -220,16 +231,13 @@ private Boolean scanTypeJob (Map params, String jobId) { return true; } - private Boolean updateTrafficJob(File file, String jobId, String action) { + private boolean updateTrafficJob(File file, String jobId, String action) { if(loginExpired() || file == null) return false; String request_url = m_authProvider.getServer() + String.format(ASE_UPDTRAFFIC, jobId, action); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); List parts = new ArrayList(); @@ -264,10 +272,7 @@ private boolean updateAgentServer (Map params, String jobId ) { return false; String request_url = m_authProvider.getServer() + String.format(ASE_UPDTAGENT, jobId, params.get("agentServer")); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); @@ -292,16 +297,82 @@ private Map getUpdatescantJobParams(String scantNodeXpath, String //apiParams.put("allowExploreDataUpdate", "0"); return apiParams; } + + private boolean createPostmanCollectionJob(Map params, String jobId) { + + if(loginExpired() || params == null) + return false; + + String request_url = m_authProvider.getServer() + String.format(ASE_POSTMAN_COLLECTION, jobId); + Map request_headers = getRequestHeaders(); + + List parts = new ArrayList(); + HttpsClient client = new HttpsClient(); + + try { + addAllFileParts(params, parts); + + if(params.containsKey("additionalDomains")) { + parts.add(new HttpPart("additionalDomains", params.get("additionalDomains"))); //$NON-NLS-1$ + } + parts.add(new HttpPart("asc_xsrf_token", request_headers.get("asc_xsrf_token"))); //$NON-NLS-1$ + + + HttpResponse response = client.postMultipart(request_url, request_headers, parts); + int status = response.getResponseCode(); + if (status == HttpsURLConnection.HTTP_OK) { + m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(UPDATE_POSTMAN_COLLECTION_SUCCESS, jobId))); + } else { + JSONObject json = (JSONObject) response.getResponseBodyAsJSON(); + if(json != null && json.has("errorMessage")){ + m_progress.setStatus(new Message(Message.ERROR, json.getString("errorMessage"))); + } else { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_UPDATE_JOB, status))); + } + return false; + } + } catch(IOException | JSONException e) { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_UPDATE_JOB, e.getLocalizedMessage()))); + return false; + } + return true; + } private File getFile(String fileLocation) { if(fileLocation != null && new File(fileLocation).isFile()) { File file = new File(fileLocation); return file; } + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_FILE_NOT_FOUND, fileLocation))); return null; } - - private boolean runScanJob(String jobId) { + + private void addAllFileParts(Map params, List parts) throws IOException { + String[][] filePartMappings = { + {"postmanCollectionFile", "postmanCollectionFile"}, + {"environmentalVariablesFile", "postmanEnvironmentFile"}, + {"globalVariablesFile", "postmanGlobalFile"}, + {"additionalFiles", "postmanAdditionalFiles"} + }; + + for (String[] mapping : filePartMappings) { + addFilePart(params, parts, mapping[0], mapping[1]); + } + } + + + private void addFilePart(Map params, List parts, String paramKey, String partName) throws IOException { + String filePath = params.get(paramKey); + if (filePath != null && !filePath.isEmpty()) { + File file = getFile(filePath); + if (file != null) { + parts.add(new HttpPart(partName, file, "multipart/form-data")); //$NON-NLS-1$ + } + } + } + + + private boolean runScanJob(String jobId) { if(loginExpired()) return false; @@ -311,10 +382,7 @@ private boolean runScanJob(String jobId) { String eTag = ""; eTag = getEtag(jobId); String request_url = m_authProvider.getServer() + String.format(ASE_RUN_JOB_ACTION, jobId); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); request_headers.put("If-Match", eTag); Map params= new HashMap<>(); params.put("type", "run"); @@ -342,10 +410,7 @@ private String getEtag(String jobId) { return null; String request_url = m_authProvider.getServer() + String.format(ASE_GET_JOB, jobId); - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ - request_headers.put(CHARSET, UTF8); - request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); @@ -380,7 +445,7 @@ public JSONObject getScanDetails(String jobId) throws IOException, JSONException return null; String reportPackId=getReportPackId(jobId); String request_url = m_authProvider.getServer() + String.format(ASE_REPORTS, reportPackId); - Map request_headers = m_authProvider.getAuthorizationHeader(true); + Map request_headers = getRequestHeaders(); HttpsClient client = new HttpsClient(); HttpResponse response = client.get(request_url, request_headers, null); @@ -399,6 +464,16 @@ public JSONArray getNonCompliantIssues(String scanId) throws IOException, JSONEx throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + @Override + public JSONArray getNonCompliantIssuesUsingExecutionId(String executionId) throws IOException, JSONException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String rescan(String scanId, Map params) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + @Override public IAuthenticationProvider getAuthenticationProvider() { return m_authProvider; @@ -409,10 +484,15 @@ public void setProgress(IProgress progress) { m_progress = progress; } + @Override + public JSONArray getBaseScanDetails(String scanId) { + return null; + } + private String getReportPackId(String jobId) { return String.valueOf(Integer.parseInt(jobId)+1); - // Uncomment the below code when you figure out how to parse the reponse. - // currently the reponse is returned as array which makes no sense. + // Uncomment the below code when you figure out how to parse the response. + // currently the response is returned as array which makes no sense. /*IAuthenticationProvider authProvider = m_scanProvider.getAuthenticationProvider(); if(authProvider.isTokenExpired()) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_LOGIN_EXPIRED))); @@ -501,4 +581,12 @@ private JSONObject getResultJson(HttpResponse response) { } return null; } + + private Map getRequestHeaders() { + Map request_headers = m_authProvider.getAuthorizationHeader(true); + request_headers.put(CONTENT_TYPE, "application/json; utf-8"); //$NON-NLS-1$ + request_headers.put(CHARSET, UTF8); + request_headers.put("Accept", "application/json"); //$NON-NLS-1$ //$NON-NLS-2$ + return request_headers; + } } diff --git a/src/main/java/com/hcl/appscan/sdk/scan/ASEScanType.java b/src/main/java/com/hcl/appscan/sdk/scan/ASEScanType.java new file mode 100644 index 00000000..5fb492ce --- /dev/null +++ b/src/main/java/com/hcl/appscan/sdk/scan/ASEScanType.java @@ -0,0 +1,48 @@ +/** + * © Copyright HCL Technologies Ltd. 2025. + * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.hcl.appscan.sdk.scan; + +import com.hcl.appscan.sdk.CoreConstants; + +public enum ASEScanType { + FULL_SCAN(CoreConstants.FULL_SCAN, "1"), + TEST_ONLY(CoreConstants.TEST_ONLY, "3"), + POSTMAN_COLLECTION(CoreConstants.POSTMAN_COLLECTION, "4"); + + private final String type; + private final String code; + + ASEScanType(String type, String code) { + this.type = type; + this.code = code; + } + + public String getType() { + return type; + } + + public String getCode() { + return code; + } + + public static String scanTypeCode(String type) { + for (ASEScanType scanType : values()) { + if (scanType.getType().equalsIgnoreCase(type)) { + return scanType.getCode(); + } + } + return type; // fallback for unknown type + } + + public static String scanTypeName(String code) { + for (ASEScanType scanType : values()) { + if (scanType.getCode().equals(code)) { + return scanType.getType(); + } + } + return ""; // fallback for unknown code + } +} diff --git a/src/main/java/com/hcl/appscan/sdk/scan/CloudScanServiceProvider.java b/src/main/java/com/hcl/appscan/sdk/scan/CloudScanServiceProvider.java index 0618f49c..bc9dfbfa 100644 --- a/src/main/java/com/hcl/appscan/sdk/scan/CloudScanServiceProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/scan/CloudScanServiceProvider.java @@ -14,9 +14,11 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.HashMap; import javax.net.ssl.HttpsURLConnection; +import com.hcl.appscan.sdk.logging.DefaultProgress; import com.hcl.appscan.sdk.scanners.dynamic.DASTConstants; import com.hcl.appscan.sdk.utils.FileUtil; import com.hcl.appscan.sdk.utils.ServiceUtil; @@ -49,54 +51,85 @@ public CloudScanServiceProvider(IProgress progress, IAuthenticationProvider auth m_progress = progress; m_authProvider = authProvider; } + + public CloudScanServiceProvider(IAuthenticationProvider authProvider) { + this(new DefaultProgress(), authProvider); + } @Override public String createAndExecuteScan(String type, Map params) { - if(loginExpired() || (params.containsKey(APP_ID) && !verifyApplication(params.get(APP_ID).toString()))) { + String requestUrl = m_authProvider.getServer() + String.format(API_SCANNER, type); + String progressMessage = Messages.getMessage(CREATE_SCAN_SUCCESS,type.toUpperCase()); + String overviewMessage = Messages.getMessage(SCAN_OVERVIEW,type.toUpperCase()); + return executeScan(requestUrl, params, progressMessage, overviewMessage); + } + + @Override + public String rescan(String scanId, Map params) { + String requestUrl = m_authProvider.getServer() + String.format(API_RESCAN, scanId); + + Map updateParams = new HashMap<>(); + updateParams.put("Name", params.remove(CoreConstants.SCAN_NAME)); + updateParams.put("EnableMailNotifications", params.remove(CoreConstants.EMAIL_NOTIFICATION)); + updateParams.put("FullyAutomatic", params.remove("FullyAutomatic")); + updateScanData(updateParams, scanId); + + String progressMessage = Messages.getMessage(RESCAN_SUCCESS); + String overviewMessage = Messages.getMessage(RESCAN_OVERVIEW); + return executeScan(requestUrl, params, progressMessage, overviewMessage); + } + + //private method to handle common logic + private String executeScan(String requestUrl, Map params, String successMessageKey, String overviewMessageKey) { + if (loginExpired() || (params.containsKey(APP_ID) && !verifyApplication(params.get(APP_ID).toString()))) { return null; } - Map request_headers = m_authProvider.getAuthorizationHeader(true); + Map requestHeaders = m_authProvider.getAuthorizationHeader(true); HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); try { - request_headers.put("Content-Type", "application/json"); - request_headers.put("accept", "application/json"); - String request_url = m_authProvider.getServer() + String.format(API_SCANNER, type); - - HttpResponse response = client.post(request_url,request_headers,params); - int status = response.getResponseCode(); + requestHeaders.put("Content-Type", "application/json"); + requestHeaders.put("accept", "application/json"); + HttpResponse response = client.post(requestUrl, requestHeaders, params); + int status = response.getResponseCode(); JSONObject json = (JSONObject) response.getResponseBodyAsJSON(); if (status == HttpsURLConnection.HTTP_CREATED || status == HttpsURLConnection.HTTP_OK) { - String scanId = json.getString(ID); - m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(CREATE_SCAN_SUCCESS, type.toUpperCase(), scanId))); - String scanOverviewUrl = m_authProvider.getServer() + "/main/myapps/" + params.get(CoreConstants.APP_ID) + "/scans/" + scanId; - m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(SCAN_OVERVIEW, type.toUpperCase(), scanOverviewUrl))); - return scanId; + String id = json.getString(ID); + String scanOverviewUrl; + if(params.containsKey(SCAN_ID)) { + String scanId= params.get(SCAN_ID); + scanOverviewUrl = m_authProvider.getServer() + "/main/myapps/" + params.get(CoreConstants.APP_ID) + "/scans/" + scanId; + } else { + scanOverviewUrl = m_authProvider.getServer() + "/main/myapps/" + params.get(CoreConstants.APP_ID) + "/scans/" + id; + } + m_progress.setStatus(new Message(Message.INFO, successMessageKey + " " + id)); + m_progress.setStatus(new Message(Message.INFO, overviewMessageKey + " " + scanOverviewUrl)); + return id; } else if (json != null && json.has(MESSAGE)) { String errorResponse = json.getString(MESSAGE); - if(json.has(FORMAT_PARAMS) && !json.isNull(FORMAT_PARAMS)) { + if (json.has(FORMAT_PARAMS) && !json.isNull(FORMAT_PARAMS)) { JSONArray jsonArray = json.getJSONArray(FORMAT_PARAMS); - if(jsonArray != null){ + if (jsonArray != null) { String[] messageParams = new String[jsonArray.size()]; for (int i = 0; i < jsonArray.size(); i++) { - messageParams[i] = (String)jsonArray.get(i); + messageParams[i] = (String) jsonArray.get(i); } errorResponse = MessageFormat.format(errorResponse, (Object[]) messageParams); } } m_progress.setStatus(new Message(Message.ERROR, errorResponse)); - } - else + } else { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, status))); - } catch(IOException | JSONException e) { + } + } catch (IOException | JSONException e) { m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_SUBMITTING_SCAN, e.getLocalizedMessage()))); } return null; - } - + } + @Override public String submitFile(File file) throws IOException { if(loginExpired()) @@ -169,40 +202,74 @@ public JSONObject getScanDetails(String scanId) throws IOException, JSONExceptio return null; } + + public JSONObject getScanDetails(String type, String scanId) { + if (loginExpired()) { + return null; + } + + String request_url = m_authProvider.getServer() + String.format(API_SCANNER_DETAILS, ServiceUtil.scanTypeShortForm(type), scanId); + Map request_headers = m_authProvider.getAuthorizationHeader(true); + request_headers.put("accept", "application/json"); + request_headers.put("Content-Type", "application/json"); + + HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); + try { + HttpResponse response = client.get(request_url, request_headers, null); + + if (response.isSuccess()) { + return (JSONObject) response.getResponseBodyAsJSON(); + } + } catch (IOException | JSONException e) { + // Ignore and move on. + } + + return null; + } @Override public JSONArray getNonCompliantIssues(String scanId) throws IOException, JSONException { - if(loginExpired()) - return null; - - String request_url = m_authProvider.getServer() + String.format(API_ISSUES_COUNT, "Scan", scanId); - request_url +="?applyPolicies=All&%24filter=Status%20eq%20%27Open%27%20or%20Status%20eq%20%27InProgress%27%20or%20Status%20eq%20%27Reopened%27%20or%20Status%20eq%20%27New%27&%24apply=groupby%28%28Status%2CSeverity%29%2Caggregate%28%24count%20as%20N%29%29"; - Map request_headers = m_authProvider.getAuthorizationHeader(true); - request_headers.put("Content-Type", "application/json; charset=UTF-8"); - request_headers.put("Accept", "application/json"); - - HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); - HttpResponse response = client.get(request_url, request_headers, null); - - if (response.isSuccess()) { - JSONObject json = (JSONObject) response.getResponseBodyAsJSON(); - return (JSONArray) json.getJSONArray("Items"); - } + return getNonCompliantIssues("Scan", scanId); + } - if (response.getResponseCode() == HttpsURLConnection.HTTP_BAD_REQUEST) - m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_INFO, "Scan", scanId))); - else { - JSONObject obj=(JSONObject)response.getResponseBodyAsJSON(); - if (obj!=null && obj.has(MESSAGE)){ - m_progress.setStatus(new Message(Message.ERROR, obj.getString(MESSAGE))); - } - else { - m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS, response.getResponseCode()))); - } - } - - return null; + @Override + public JSONArray getNonCompliantIssuesUsingExecutionId(String executionId) throws IOException, JSONException { + return getNonCompliantIssues("ScanExecution", executionId); } + + //private method to handle common logic + private JSONArray getNonCompliantIssues(String idType, String id) throws IOException, JSONException { + if (loginExpired()) + return null; + + String requestUrl = m_authProvider.getServer() + String.format(API_ISSUES_COUNT, idType, id); + requestUrl += "?applyPolicies=All&%24filter=Status%20eq%20%27Open%27%20or%20Status%20eq%20%27InProgress%27%20or%20Status%20eq%20%27Reopened%27&%24apply=groupby%28%28Status%2CSeverity%29%2Caggregate%28%24count%20as%20N%29%29"; + + Map requestHeaders = m_authProvider.getAuthorizationHeader(true); + requestHeaders.put("Content-Type", "application/json; charset=UTF-8"); + requestHeaders.put("Accept", "application/json"); + + HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); + HttpResponse response = client.get(requestUrl, requestHeaders, null); + + if (response.isSuccess()) { + JSONObject json = (JSONObject) response.getResponseBodyAsJSON(); + return (JSONArray) json.getJSONArray("Items"); + } + + if (response.getResponseCode() == HttpsURLConnection.HTTP_BAD_REQUEST) { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_INFO, idType, id))); + } else { + JSONObject obj = (JSONObject) response.getResponseBodyAsJSON(); + if (obj != null && obj.has(MESSAGE)) { + m_progress.setStatus(new Message(Message.ERROR, obj.getString(MESSAGE))); + } else { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_GETTING_DETAILS, response.getResponseCode()))); + } + } + + return null; + } @Override public IAuthenticationProvider getAuthenticationProvider() { @@ -231,4 +298,51 @@ private boolean verifyApplication(String appId) { public void setProgress(IProgress progress) { m_progress = progress; } + + @Override + public JSONArray getBaseScanDetails(String scanId) { + if (loginExpired()) { + return null; + } + + String request_url = m_authProvider.getServer() + String.format(API_EXECUTION_DETAILS, scanId); + request_url += "?$filter=IsValidForIncremental%20eq%20true&%24select=Id%2C%20CreatedAt%2C%20IsValidForIncremental&%24orderby=CreatedAt%20desc"; + Map request_headers = m_authProvider.getAuthorizationHeader(true); + request_headers.put("accept", "application/json"); + request_headers.put("Content-Type", "application/json"); + + HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); + try { + HttpResponse response = client.get(request_url, request_headers, null); + + if (response.isSuccess()) { + return (JSONArray) response.getResponseBodyAsJSON(); + } + } catch (IOException | JSONException e) { + // Ignore and move on. + } + + return null; + } + + public void updateScanData(Map params, String scanId) { + if (loginExpired()) { + return; + } + + String request_url = m_authProvider.getServer() + String.format(API_SCANNER,scanId); + Map request_headers = m_authProvider.getAuthorizationHeader(true); + request_headers.put("accept", "application/json"); + request_headers.put("Content-Type", "application/json"); + + HttpClient client = new HttpClient(m_authProvider.getProxy(), m_authProvider.getacceptInvalidCerts()); + try { + HttpResponse response = client.put(request_url, request_headers, params); + if (response.getResponseCode() == HttpsURLConnection.HTTP_NO_CONTENT) { + m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(UPDATE_JOB))); + } + } catch (IOException | JSONException e) { + m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_UPDATE_JOB, e.getLocalizedMessage()))); + } + } } diff --git a/src/main/java/com/hcl/appscan/sdk/scan/IScan.java b/src/main/java/com/hcl/appscan/sdk/scan/IScan.java index a2a50adf..e3f0fc40 100644 --- a/src/main/java/com/hcl/appscan/sdk/scan/IScan.java +++ b/src/main/java/com/hcl/appscan/sdk/scan/IScan.java @@ -8,7 +8,6 @@ import com.hcl.appscan.sdk.error.InvalidTargetException; import com.hcl.appscan.sdk.error.ScannerException; -import com.hcl.appscan.sdk.logging.IProgress; import com.hcl.appscan.sdk.results.IResultsProvider; public interface IScan { diff --git a/src/main/java/com/hcl/appscan/sdk/scan/IScanServiceProvider.java b/src/main/java/com/hcl/appscan/sdk/scan/IScanServiceProvider.java index 42d9eb41..1adb2c2c 100644 --- a/src/main/java/com/hcl/appscan/sdk/scan/IScanServiceProvider.java +++ b/src/main/java/com/hcl/appscan/sdk/scan/IScanServiceProvider.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017,2018. + * © Copyright HCL Technologies Ltd. 2017,2024. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -15,7 +15,6 @@ import org.apache.wink.json4j.JSONObject; import com.hcl.appscan.sdk.auth.IAuthenticationProvider; -import com.hcl.appscan.sdk.http.HttpResponse; import com.hcl.appscan.sdk.logging.IProgress; /** @@ -31,6 +30,15 @@ public interface IScanServiceProvider { * @return The id of the submitted scan, if successful. Otherwise, null. */ public String createAndExecuteScan(String type, Map params); + + /** + * Initiates a Rescan + * + * @param scanId The ID of the parent scan on which the rescan will be executed. + * @param params A Map of rescan parameters. + * @return The id of the submitted rescan, if successful. Otherwise, returns null. + */ + public String rescan(String scanId, Map params); /** * Submits a file for scanning. @@ -60,7 +68,16 @@ public interface IScanServiceProvider { * @throws JSONException If an error occurs. */ public JSONArray getNonCompliantIssues(String scanId) throws IOException, JSONException; - + + /** + * Gets the non compliant issues in JSON format. + * + * @param executionId The id of the scan execution to retrieve all the non compliant issues + * @return JSONArray containing the issues as JSON objects. + * @throws IOException If an error occurs. + * @throws JSONException If an error occurs. + */ + public JSONArray getNonCompliantIssuesUsingExecutionId(String executionId) throws IOException, JSONException; /** * Gets the {@link IAuthenticationProvider} used to authenticate with a scanning service. * @@ -74,4 +91,12 @@ public interface IScanServiceProvider { * @param progress The {@link IProgress}. */ public void setProgress(IProgress progress); + + /** + * Fetch the details of all the executions of a scan. + * + * @param scanId The scanId to test + * @return JSONArray. + */ + public JSONArray getBaseScanDetails(String scanId); } diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/ASoCScan.java b/src/main/java/com/hcl/appscan/sdk/scanners/ASoCScan.java index cb323d45..1f0f3371 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/ASoCScan.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/ASoCScan.java @@ -25,6 +25,8 @@ public abstract class ASoCScan implements IScan, ScanConstants, Serializable { private String m_target; private String m_scanId; + private String m_executionId; + private boolean m_rescan; private IProgress m_progress; private IScanServiceProvider m_serviceProvider; private Map m_properties; @@ -40,12 +42,17 @@ public ASoCScan(Map properties, IProgress progress, IScanService m_properties.put(CoreConstants.SCAN_NAME, getType() + SystemUtil.getTimeStamp()); m_progress = progress; m_serviceProvider = provider; + m_rescan = m_properties.containsKey(CoreConstants.SCAN_ID); } @Override public String getScanId() { return m_scanId; } + + public String getExecutionId() { + return m_executionId; + } @Override public String getName() { @@ -62,7 +69,7 @@ public IResultsProvider getResultsProvider() { @Override public IResultsProvider getResultsProvider(boolean nonCompliantIssues) { if(nonCompliantIssues) { - IResultsProvider provider = new NonCompliantIssuesResultProvider(m_scanId, getType(), m_serviceProvider, m_progress); + IResultsProvider provider = new NonCompliantIssuesResultProvider(m_scanId, m_executionId, getType(), m_serviceProvider, m_progress); provider.setReportFormat(getReportFormat()); return provider; } @@ -74,6 +81,18 @@ public IResultsProvider getResultsProvider(boolean nonCompliantIssues) { protected void setScanId(String id) { m_scanId = id; } + + protected void setExecutionId(String id){ + m_executionId = id; + } + + public void setRescan(boolean rescan){ + m_rescan = rescan; + } + + public boolean getRescan() { + return m_rescan; + } protected String getAppId() { return m_properties.get(CoreConstants.APP_ID); @@ -104,5 +123,9 @@ protected Map getProperties() { return m_properties; } + protected void submitRescan() { + setExecutionId(getServiceProvider().rescan(getScanId(),getProperties())); + } + public abstract String getReportFormat(); } \ No newline at end of file diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/dynamic/DASTScan.java b/src/main/java/com/hcl/appscan/sdk/scanners/dynamic/DASTScan.java index 6d357c01..abfa77f4 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/dynamic/DASTScan.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/dynamic/DASTScan.java @@ -18,7 +18,6 @@ import com.hcl.appscan.sdk.logging.DefaultProgress; import com.hcl.appscan.sdk.logging.IProgress; import com.hcl.appscan.sdk.scan.IScanServiceProvider; -import com.hcl.appscan.sdk.scan.CloudScanServiceProvider; import com.hcl.appscan.sdk.scanners.ASoCScan; import com.hcl.appscan.sdk.utils.ServiceUtil; import org.apache.wink.json4j.JSONException; @@ -94,13 +93,19 @@ public void run() throws ScannerException, InvalidTargetException { try { JSONObject propertiesJSON = createJSONForProperties(params); - setScanId(getServiceProvider().createAndExecuteScan(type, propertiesJSON)); + if (getRescan()) { + setScanId(params.get(CoreConstants.SCAN_ID)); + setExecutionId(getServiceProvider().rescan(getScanId(), propertiesJSON)); + if(getExecutionId() == null) + throw new ScannerException(Messages.getMessage(ERROR_CREATING_SCAN)); + } else { + setScanId(getServiceProvider().createAndExecuteScan(type, propertiesJSON)); + if(getScanId() == null) + throw new ScannerException(Messages.getMessage(ERROR_CREATING_SCAN)); + } } catch (JSONException e) { throw new ScannerException(Messages.getMessage(ERROR_RUNNING_SCAN, e.getLocalizedMessage())); } - - if(getScanId() == null) - throw new ScannerException(Messages.getMessage(ERROR_CREATING_SCAN)); } private JSONObject createJSONForProperties(Map params) throws JSONException { diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAClient.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAClient.java index 0684738b..6bd7e9f4 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAClient.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAClient.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017, 2024. + * © Copyright HCL Technologies Ltd. 2017, 2026. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -10,6 +10,7 @@ import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.Proxy; @@ -24,6 +25,7 @@ import com.hcl.appscan.sdk.logging.IProgress; import com.hcl.appscan.sdk.logging.Message; import com.hcl.appscan.sdk.utils.ArchiveUtil; +import com.hcl.appscan.sdk.utils.ArchiveUtilSymlinks; import com.hcl.appscan.sdk.utils.FileUtil; import com.hcl.appscan.sdk.utils.ServiceUtil; import com.hcl.appscan.sdk.utils.SystemUtil; @@ -165,8 +167,8 @@ public String getClientScript(String serverURL, String acceptInvalidCerts) throw //See if we already have the client package. String scriptPath = "bin" + File.separator + getScriptName(); //$NON-NLS-1$ File install = findClientInstall(); - - if(install != null && new File(install, scriptPath).isFile() && !shouldUpdateClient(serverURL)) + + if(install != null && new File(install, scriptPath).isFile() && !shouldUpdateClient(serverURL, Boolean.parseBoolean(acceptInvalidCerts))) return new File(install, scriptPath).getAbsolutePath(); //Download it. @@ -189,10 +191,19 @@ public String getClientScript(String serverURL, String acceptInvalidCerts) throw if(clientZip.isFile()) { m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(DOWNLOAD_COMPLETE))); m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(EXTRACTING_CLIENT))); - ArchiveUtil.unzip(clientZip, m_installDir); - m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(DONE))); } + // Handle Mac bundle release (which contains symlinks that aren't handled by the Java zip class) + if (SystemUtil.isMac()) { + ArchiveUtilSymlinks aus = new ArchiveUtilSymlinks(); + aus.unzip(clientZip, m_installDir); + } + else { + ArchiveUtil au = new ArchiveUtil(); + au.unzip(clientZip, m_installDir); + } + m_progress.setStatus(new Message(Message.INFO, Messages.getMessage(DONE))); + return new File(findClientInstall(), scriptPath).getAbsolutePath(); } @@ -215,12 +226,16 @@ public boolean majorVersionChanged() throws IOException { } } - public boolean shouldUpdateClient() throws IOException { - return shouldUpdateClient(""); - } - + public boolean shouldUpdateClient() throws IOException { + return shouldUpdateClient(""); + } + public boolean shouldUpdateClient(String serverURL) throws IOException { - String serverVersion = ServiceUtil.getSAClientVersion(m_proxy,serverURL); + return shouldUpdateClient(serverURL, false); + } + + public boolean shouldUpdateClient(String serverURL, boolean acceptInvalidCerts) throws IOException { + String serverVersion = ServiceUtil.getSAClientVersion(m_proxy,serverURL, acceptInvalidCerts); String localVersion = getLocalClientVersion(); if(compareVersions(localVersion, serverVersion) && System.getProperty(CoreConstants.SKIP_UPDATE) == null) { @@ -264,14 +279,17 @@ public boolean accept(File dir, String name) { private String getLocalClientVersion() { File versionInfo = new File(findClientInstall(), VERSION_INFO); + String version = null; BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(versionInfo)); version = reader.readLine(); //The version is the first line of the version.info file. + } catch (FileNotFoundException ignore) { + // Expected before first installation } catch (IOException e) { - m_progress.setStatus(new Message(Message.ERROR, Messages.getMessage(ERROR_CHECKING_SACLIENT_VER, e.getLocalizedMessage()))); + m_progress.setStatus(new Message(Message.WARNING, Messages.getMessage(ERROR_CHECKING_SACLIENT_VER, e.getLocalizedMessage()))); } finally { try { if(reader != null) @@ -358,34 +376,19 @@ private List getClientArgs(Map properties) { if(properties.containsKey(SECRETS_ONLY) || System.getProperty(SECRETS_ONLY) != null) { args.add(OPT_SECRETS_ONLY); } + //For AppScan 360 support. + if(Boolean.parseBoolean(properties.getOrDefault(CoreConstants.ACCEPT_INVALID_CERTS, Boolean.toString(false)))) { + args.add(OPT_ACCEPTS_SSL); + } + if(properties.get(CoreConstants.SERVER_URL) != null && properties.get(CoreConstants.SERVER_URL).trim() != "") { //$NON-NLS-1$ + args.add("-D" + CoreConstants.BLUEMIX_SERVER + "=" + properties.get(CoreConstants.SERVER_URL)); //$NON-NLS-1$ //$NON-NLS-2$ + } return args; } - private boolean compareVersions(String baseVersion, String newVersion) { - if(baseVersion == null) - return true; - - if(baseVersion != null && newVersion != null) { - String[] base = baseVersion.split("\\."); //$NON-NLS-1$ - String[] next = newVersion.split("\\."); //$NON-NLS-1$ - - try { - for(int iter = 0; iter < base.length && iter < next.length; iter++) { - int lVersion = Integer.parseInt(base[iter]); - int sVersion = Integer.parseInt(next[iter]); - - if (((iter==0) && lVersion params = getProperties(); - params.put(FILE_ID, fileId); - - submitScan(); - if(getScanId() == null) - throw new ScannerException(Messages.getMessage(ERROR_SUBMITTING_IRX)); + + if (getRescan()) { + setScanId(params.get(CoreConstants.SCAN_ID)); + params.put(CoreConstants.FILE_ID, fileId); + submitRescan(); + if(getExecutionId() == null) + throw new ScannerException(Messages.getMessage(ERROR_CREATING_SCAN)); + } else { + params.put(FILE_ID, fileId); + submitScan(); + if(!getProperties().containsKey(CoreConstants.INCLUDE_SCA) && getScanId() == null) + throw new ScannerException(Messages.getMessage(ERROR_CREATING_SCAN)); + } } - + protected void submitScan() { setScanId(getServiceProvider().createAndExecuteScan(STATIC_ANALYZER, getProperties())); } diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SASTScanManager.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SASTScanManager.java index b338bdb4..e4438117 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SASTScanManager.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SASTScanManager.java @@ -27,6 +27,7 @@ import com.hcl.appscan.sdk.scanners.sast.xml.ModelWriter; import com.hcl.appscan.sdk.scanners.sast.xml.XmlWriter; import com.hcl.appscan.sdk.scanners.sca.SCAScan; +import com.hcl.appscan.sdk.utils.ServiceUtil; import com.hcl.appscan.sdk.utils.SystemUtil; public class SASTScanManager implements IScanManager{ @@ -38,7 +39,8 @@ public class SASTScanManager implements IScanManager{ private boolean m_isOpenSourceOnlyEnabled = false; private boolean m_isSourceCodeOnlyEnabled = false; private boolean m_isStaticAnalysisOnlyEnabled = false; - private boolean m_isSecretsScanningEnabled = false; + private boolean m_isSecretsScanningDisabled = false; + private boolean m_isSecretsScanningEnabled = false; private boolean m_isSecretsScanningOnlyEnabled = false; public SASTScanManager(String workingDir) { @@ -110,12 +112,22 @@ public void setIsThirdPartyScanningEnabled(boolean isThirdPartyScanningEnabled) m_isThirdPartyScanningEnabled = isThirdPartyScanningEnabled; } + /** + * Disables scanning for secrets. + * @param isSecretsScanningDisabled - True to skip scanning for secrets vulnerabilities. + */ + public void setIsSecretsScanningDisabled(boolean isSecretsScanningDisabled) { + m_isSecretsScanningDisabled = isSecretsScanningDisabled; + m_isSecretsScanningEnabled = !isSecretsScanningDisabled; + } + /** * Enables scanning for secrets. * @param isSecretsScanningEnabled - True to scan for secrets vulnerabilities. */ public void setIsSecretsScanningEnabled(boolean isSecretsScanningEnabled) { - m_isSecretsScanningEnabled = isSecretsScanningEnabled; + m_isSecretsScanningDisabled = !isSecretsScanningEnabled; + m_isSecretsScanningEnabled = isSecretsScanningEnabled; } /** @@ -160,7 +172,7 @@ public void createConfig(boolean useRelativeTargetPaths) throws AppScanException try { ModelWriter writer = new XmlWriter(useRelativeTargetPaths); writer.initWriters(new File(m_workingDirectory)); - writer.visit(m_targets, m_isThirdPartyScanningEnabled, m_isOpenSourceOnlyEnabled, m_isSourceCodeOnlyEnabled, m_isStaticAnalysisOnlyEnabled, m_isSecretsScanningEnabled, m_isSecretsScanningOnlyEnabled); + writer.visit(m_targets, m_isThirdPartyScanningEnabled, m_isOpenSourceOnlyEnabled, m_isSourceCodeOnlyEnabled, m_isStaticAnalysisOnlyEnabled, m_isSecretsScanningDisabled, m_isSecretsScanningEnabled, m_isSecretsScanningOnlyEnabled); writer.write(); } catch (IOException | TransformerException e) { throw new AppScanException(e.getLocalizedMessage(), e); @@ -172,12 +184,12 @@ private String getDefaultScanName() { } private void createScan(Map properties, IProgress progress, IScanServiceProvider provider) { - if(m_isStaticAnalysisOnlyEnabled) { - m_scan = new SASTScan(properties, progress, provider); - } - else if(m_isOpenSourceOnlyEnabled ) { + if(m_isOpenSourceOnlyEnabled ) { m_scan = new SCAScan(properties, progress, provider); } + else if(m_isStaticAnalysisOnlyEnabled || (provider != null && !ServiceUtil.hasScaEntitlement(provider.getAuthenticationProvider()))) { + m_scan = new SASTScan(properties, progress, provider); + } else { m_scan = new SAST_SCA_Scan(properties, progress, provider); } diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAST_SCA_Scan.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAST_SCA_Scan.java index 4027cac5..0424c2ca 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAST_SCA_Scan.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/SAST_SCA_Scan.java @@ -44,11 +44,10 @@ public void run() throws ScannerException, InvalidTargetException { //Avoid generating 2 .irx files. return; } - else { - m_sastScanId = getScanId(); - m_scaScan.run(); - m_scaScanId = m_scaScan.getScanId(); - } + + m_sastScanId = getScanId(); + m_scaScan.run(); + m_scaScanId = m_scaScan.getScanId(); } @Override diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/targets/GenericTarget.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/targets/GenericTarget.java index c5e2db45..824ecad2 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/targets/GenericTarget.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/targets/GenericTarget.java @@ -1,7 +1,6 @@ package com.hcl.appscan.sdk.scanners.sast.targets; import java.io.File; -import java.util.Collections; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/IModelXMLConstants.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/IModelXMLConstants.java index a7e86076..69f36aba 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/IModelXMLConstants.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/IModelXMLConstants.java @@ -25,6 +25,7 @@ public interface IModelXMLConstants { String A_SOURCE_CODE_ONLY = "sourceCodeOnly"; //$NON-NLS-1$ String A_STATIC_ANALYSIS_ONLY = "staticAnalysisOnly"; //$NON-NLS-1$ String A_SECRETS_ENABLED = "enableSecrets"; //$NON-NLS-1$ + String A_SECRETS_DISABLED = "noSecrets"; //$NON-NLS-1$ String A_SECRETS_ONLY = "secretsOnly"; //$NON-NLS-1$ //Java diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/ModelWriter.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/ModelWriter.java index 8c658f99..dc162c92 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/ModelWriter.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/ModelWriter.java @@ -120,5 +120,5 @@ private void initDocumentBuilder() throws ParserConfigurationException { public abstract String getOutputLocation(); - public abstract void visit(List targets, boolean isThirdPartyScanningEnabled, boolean isOpenSourceOnlyEnabled, boolean isSourceCodeOnlyEnabled, boolean isStaticAnalysisOnlyEnabled, boolean isSecretsScanningEnabled, boolean isSecretsScanningOnlyEnabled); + public abstract void visit(List targets, boolean isThirdPartyScanningEnabled, boolean isOpenSourceOnlyEnabled, boolean isSourceCodeOnlyEnabled, boolean isStaticAnalysisOnlyEnabled, boolean isSecretsScanningDisabled, boolean isSecretsScanningEnabled, boolean isSecretsScanningOnlyEnabled); } diff --git a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/XmlWriter.java b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/XmlWriter.java index df3d02bb..805cf970 100644 --- a/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/XmlWriter.java +++ b/src/main/java/com/hcl/appscan/sdk/scanners/sast/xml/XmlWriter.java @@ -52,7 +52,7 @@ public void initWriters(File directory) throws IOException { @Override public void visit(List targets, boolean isThirdPartyScanningEnabled, - boolean isOpenSourceOnlyEnabled, boolean isSourceCodeOnlyEnabled, boolean isStaticAnalysisOnlyEnabled, boolean isSecretsScanningEnabled, boolean isSecretsScanningOnlyEnabled) { + boolean isOpenSourceOnlyEnabled, boolean isSourceCodeOnlyEnabled, boolean isStaticAnalysisOnlyEnabled, boolean isSecretsScanningDisabled, boolean isSecretsScanningEnabled, boolean isSecretsScanningOnlyEnabled) { m_config.beginElement(E_CONFIGURATION); if (isThirdPartyScanningEnabled) { @@ -70,11 +70,13 @@ public void visit(List targets, boolean isThirdPartyScanningEnabled if (isStaticAnalysisOnlyEnabled) { m_config.setAttribute(A_STATIC_ANALYSIS_ONLY, "true"); } - + + if (isSecretsScanningDisabled) { + m_config.setAttribute(A_SECRETS_DISABLED, "true"); + } if (isSecretsScanningEnabled) { m_config.setAttribute(A_SECRETS_ENABLED, "true"); } - if (isSecretsScanningOnlyEnabled) { m_config.setAttribute(A_SECRETS_ONLY, "true"); } @@ -122,6 +124,7 @@ public void write() throws TransformerException { m_config.write(m_transformer); } + /** * Returns the location of the generated configuration file. * @return The location of the generated configuration file. diff --git a/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtil.java b/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtil.java index aad9f8c0..ad11fa49 100644 --- a/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtil.java +++ b/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtil.java @@ -16,9 +16,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; - import com.hcl.appscan.sdk.Messages; -import com.hcl.appscan.sdk.scanners.sast.SASTConstants; public class ArchiveUtil { @@ -123,9 +121,6 @@ public static void zipFileOrFolder(File fileToZip, File zipFile) throws IOExcept } private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException { - if (fileToZip.getName().startsWith(".")) { - return; - } if (fileToZip.isDirectory()) { File[] children = fileToZip.listFiles(); for (File childFile : children) { diff --git a/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtilSymlinks.java b/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtilSymlinks.java new file mode 100644 index 00000000..78403a66 --- /dev/null +++ b/src/main/java/com/hcl/appscan/sdk/utils/ArchiveUtilSymlinks.java @@ -0,0 +1,116 @@ +/************************************************************************* +* IBM and/or HCL Confidential +* AppScan Static Analyzer +* (c) Copyright IBM Corp. 2013, 2017 All Rights Reserved. +* (c) Copyright HCL Technologies, Ltd. 2023, 2026 All Rights Reserved. +* +* The source code for this program is not published or otherwise +* divested of its trade secrets, irrespective of what has been +* deposited with the U.S. Copyright Office. +*/ + +package com.hcl.appscan.sdk.utils; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.io.IOUtils; + +import com.hcl.appscan.sdk.Messages; +import com.hcl.appscan.sdk.utils.SystemUtil; + +/** + * Utility to unzip Zip archives that contain symlinks. + */ +public class ArchiveUtilSymlinks { + + private static Boolean processEntry(ZipFile zipFile, ZipArchiveEntry entry, File dest, boolean setPermissions) throws IOException { + try { + final File f = new File(dest, entry.getName()); + + if (entry.isDirectory()) { + if (!f.isDirectory() && !f.mkdirs()) { + return false; + } + } + else { + final File parent = f.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + return false; + } + + if (entry.isUnixSymlink()) { + // Skip if Windows (or make copy or abort ?) + if (! SystemUtil.isWindows()) { + final String target = zipFile.getUnixSymlink(entry); + if (target == null) { + return false; + } + final Path targetPath = Paths.get(target); + Files.createSymbolicLink(f.toPath(), targetPath); + } + } + else { + try ( InputStream content = zipFile.getInputStream(entry); + OutputStream o = Files.newOutputStream(f.toPath()) ) + { + IOUtils.copyLarge(content, o); + } + } + } + + if (setPermissions) { + //Set rwx permissions + f.setExecutable(true, false); + f.setReadable(true, false); + f.setWritable(true); + } + + } catch(IOException e) { + throw new IOException(e); + } + return true; + } + + /** + * Unzip an archive, immediately halting upon error. + * + * @param source The source archive. + * @param dest The destination directory to unzip to. + * @throws IOException If an error occurs during the unzip operation. + */ + public void unzip(File source, File dest) throws IOException { + + // See https://commons.apache.org/proper/commons-compress/examples.html + // and https://issues.apache.org/jira/browse/COMPRESS-689 for why this + // handling must use Apache's ZipFile rather than ArchiveInputStream/ZipArchiveInputStream + try ( ZipFile zipFile = ZipFile.builder().setFile(source).get() ) + { + Enumeration entries = zipFile.getEntries(); + Collections.list(entries).forEach(entry -> { + try { + if (! processEntry(zipFile, entry, dest, true)) { + final File f = new File(dest, entry.getName()); + throw new IOException(Messages.getMessage("err.invalid.path", f.getPath())); //$NON-NLS-1$ + } + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + catch (IOException | UncheckedIOException e) { + throw new IOException(e); + } + } +} diff --git a/src/main/java/com/hcl/appscan/sdk/utils/ServiceUtil.java b/src/main/java/com/hcl/appscan/sdk/utils/ServiceUtil.java index a57c0527..a6f1a59a 100644 --- a/src/main/java/com/hcl/appscan/sdk/utils/ServiceUtil.java +++ b/src/main/java/com/hcl/appscan/sdk/utils/ServiceUtil.java @@ -1,6 +1,6 @@ /** * © Copyright IBM Corporation 2016. - * © Copyright HCL Technologies Ltd. 2017, 2020, 2024. + * © Copyright HCL Technologies Ltd. 2017, 2026. * LICENSE: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 */ @@ -10,10 +10,12 @@ import java.io.IOException; import java.net.Proxy; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.net.ssl.HttpsURLConnection; -import com.hcl.appscan.sdk.http.HttpsClient; +import org.apache.wink.json4j.JSONArray; import org.apache.wink.json4j.JSONArtifact; import org.apache.wink.json4j.JSONException; import org.apache.wink.json4j.JSONObject; @@ -47,11 +49,18 @@ public static void getSAClientUtil(File destination, Proxy proxy) throws IOExcep * * @param destination The file to save the package to. * @param proxy The proxy for the connection, if required. + * @param serverURL The URL of the server. + * @param acceptInvalidCerts To allow untrusted connection. * @throws IOException If an error occurs. */ public static void getSAClientUtil(File destination, Proxy proxy, String serverURL, String acceptInvalidCerts) throws IOException { - String request_url = requiredServerURL(serverURL); - request_url += String.format(API_SACLIENT_DOWNLOAD, SystemUtil.getOS()); + + // Use override URL for testing + String request_url = System.getenv("SACLIENTUTIL_DOWNLOAD_URL"); + if (request_url == null) { + request_url = requiredServerURL(serverURL); + request_url += String.format(API_SACLIENT_DOWNLOAD, SystemUtil.getOS()); + } HttpClient client = new HttpClient(proxy,acceptInvalidCerts.equals("true")); HttpResponse response = client.get(request_url, null, null); @@ -84,22 +93,34 @@ public static String getSAClientVersion() throws IOException { return getSAClientVersion(Proxy.NO_PROXY); } - public static String getSAClientVersion(Proxy proxy) throws IOException { - return getSAClientVersion(proxy, ""); - } + public static String getSAClientVersion(Proxy proxy) throws IOException { + return getSAClientVersion(proxy, ""); + } + + public static String getSAClientVersion(Proxy proxy, String serverURL) throws IOException { + return getSAClientVersion(Proxy.NO_PROXY, serverURL, false); + } /** * Gets the latest available version of the SAClientUtil package used for running static analysis. * * @param proxy The {@link Proxy} to use. + * @param serverURL The URL of the server. + * @param acceptInvalidCerts To allow the untrusted connection. * @return The current version of the package. * @throws IOException If an error occurs. */ - public static String getSAClientVersion(Proxy proxy, String serverURL) throws IOException { + public static String getSAClientVersion(Proxy proxy, String serverURL, boolean acceptInvalidCerts) throws IOException { + + final String versionEV = System.getenv("SACLIENTUTIL_DOWNLOAD_VERSION"); //$NON-NLS-1$ + if (versionEV != null) { + return versionEV; + } + String request_url = requiredServerURL(serverURL); request_url += String.format(API_SACLIENT_VERSION, SystemUtil.getOS(), "true"); - HttpClient client = new HttpClient(proxy); + HttpClient client = new HttpClient(proxy, acceptInvalidCerts); HttpResponse response = client.get(request_url, null, null); if (response.getResponseCode() == HttpsURLConnection.HTTP_OK || response.getResponseCode() == HttpsURLConnection.HTTP_CREATED) { @@ -160,8 +181,38 @@ public static boolean isValidUrl(String url, IAuthenticationProvider provider, P return false; } + + /** + * Checks if the user has entitlement to run SCA scans. + * + * @param provider The IAuthenticationProvider for authentication. + * @return true if the user has SCA entitlement. + */ + public static boolean hasScaEntitlement(IAuthenticationProvider provider) { + return hasEntitlement(SCA_TECH, provider); + } + + /** + * Checks if the user has entitlement to run SAST scans. + * + * @param provider The IAuthenticationProvider for authentication. + * @return true if the user has SAST entitlement. + */ + public static boolean hasSastEntitlement(IAuthenticationProvider provider) { + return hasEntitlement(STATIC_TECH, provider); + } + + /** + * Checks if the user has entitlement to run DAST scans. + * + * @param provider The IAuthenticationProvider for authentication. + * @return true if the user has DAST entitlement. + */ + public static boolean hasDastEntitlement(IAuthenticationProvider provider) { + return hasEntitlement(DYNAMIC_TECH, provider); + } - public static boolean activeSubscriptionsCheck(String scanType, IAuthenticationProvider provider) { + private static boolean hasEntitlement(String scanType, IAuthenticationProvider provider) { if(provider.isTokenExpired()) { return true; } @@ -169,7 +220,7 @@ public static boolean activeSubscriptionsCheck(String scanType, IAuthenticationP String request_url = provider.getServer() + API_TENANT_INFO; try { - HttpClient client = new HttpClient(provider.getProxy(), false); + HttpClient client = new HttpClient(provider.getProxy(), provider.getacceptInvalidCerts()); Map requestHeaders= provider.getAuthorizationHeader(false); requestHeaders.put("Content-Type", "application/json"); requestHeaders.put("accept", "application/json"); @@ -189,4 +240,148 @@ public static boolean activeSubscriptionsCheck(String scanType, IAuthenticationP return false; } + + /** + * Checks if the given scanId is valid for scanning. + * + * @param scanId The scanId to test. + * @param applicationId The applicationId to verify. + * @param type The scanType to verify. + * @param provider The IAuthenticationProvider for authentication. + * @return True if the scanId is valid. False is returned if the scanId is not valid, the request fails, or an exception occurs. + */ + public static boolean isValidScanId(String scanId, String applicationId, String type, IAuthenticationProvider provider) throws IOException{ + if (provider.isTokenExpired()) { + return true; + } + + String request_url = provider.getServer() + API_BASIC_DETAILS; + request_url += "?$filter=Id%20eq%20" + scanId + "&%24select=AppId%2C%20Technology"; + Map request_headers = provider.getAuthorizationHeader(true); + + HttpClient client = new HttpClient(provider.getProxy(), provider.getacceptInvalidCerts()); + try { + HttpResponse response = client.get(request_url, request_headers, null); + + if (response.isSuccess()) { + JSONObject obj = (JSONObject) response.getResponseBodyAsJSON(); + JSONArray array = (JSONArray) obj.get(ITEMS); + if (array.isEmpty()) { + return false; + } else { + JSONObject body = (JSONObject) array.getJSONObject(0); + String appId = body.getString(CoreConstants.APP_ID); + String technologyName = body.getString("Technology"); + return appId.equals(applicationId) && technologyName.equals(updatedScanType(type)); + } + } + } catch (JSONException e) { + Logger.getLogger(ServiceUtil.class.getName()).log(Level.SEVERE, null, e); + } + + return false; + } + + public static String updatedScanType(String type) { + switch (type) { + case "Static Analyzer": + return STATIC_TECH; + case "Dynamic Analyzer": + return DYNAMIC_TECH; + case CoreConstants.SOFTWARE_COMPOSITION_ANALYZER: + return SCA_TECH; + } + return type; + } + + public static String scanTypeShortForm(String type) { + switch (type) { + case "Static Analyzer": + return "Sast"; + case "Dynamic Analyzer": + return "Dast"; + case CoreConstants.SOFTWARE_COMPOSITION_ANALYZER: + return "Sca"; + } + return type; + } + + /** + * Fetch the build version of the A360 server. + * + * @param provider The IAuthenticationProvider for authentication. + * @return The build server of the server. + */ + public static String getServiceVersion(IAuthenticationProvider provider) { + String request_url = provider.getServer() + "/assets/versions.json"; + HttpClient client = new HttpClient(provider.getProxy(), provider.getacceptInvalidCerts()); + try { + HttpResponse response = client.get(request_url, null, null); + if (response.isSuccess()) { + JSONObject body = (JSONObject) response.getResponseBodyAsJSON(); + return body.getString("MainVersion"); + } + } catch (IOException | JSONException e) { + return "0"; //$NON-NLS-1$ + } + return null; + } + + /** + * Checks if the given url is valid for scanning. + * + * @param url The url to test. + * @param provider The IAuthenticationProvider for authentication. + * @param proxy The proxy to use for the connection. + * @return True if the url is valid. False is returned if the url is not valid, the request fails, or an exception occurs. + */ + public static boolean isValidDomain(String url, String appId, IAuthenticationProvider provider, Proxy proxy) { + String request_url = provider.getServer() + API_IS_VALID_DOMAIN; + + try { + JSONObject body = new JSONObject(); + body.put(CoreConstants.APP_ID, appId); + body.put(CoreConstants.STP, url); + + HttpClient client = new HttpClient(proxy, provider.getacceptInvalidCerts()); + Map requestHeaders= provider.getAuthorizationHeader(false); + requestHeaders.put("Content-Type", "application/json"); + HttpResponse response = client.post(request_url, requestHeaders, body.toString()); + + if (response.isSuccess()) { + //response body is a boolean value + String responseContent = response.getResponseBodyAsString(); + if (responseContent != null) { + return Boolean.parseBoolean(responseContent.trim()); + } + } + } catch (IOException | JSONException e) { + // Ignore and return false. + } + return false; + } + + public static boolean compareVersions(String baseVersion, String newVersion) { + if(baseVersion == null) + return true; + + if(newVersion != null) { + String[] base = baseVersion.split("\\."); //$NON-NLS-1$ + String[] next = newVersion.split("\\."); //$NON-NLS-1$ + + try { + for(int iter = 0; iter < base.length && iter < next.length; iter++) { + int lVersion = Integer.parseInt(base[iter]); + int sVersion = Integer.parseInt(next[iter]); + + if (((iter==0) && lVersion