From 0c3394e88f6909ccb1f8176485798cf739a06cf6 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 20 Jul 2023 14:07:52 -0400 Subject: [PATCH 01/29] initial commit --- Core/ivy.xml | 13 + Core/nbproject/project.properties | 7 + Core/nbproject/project.xml | 44 +- .../autopsy/ctapi/CTCloudException.java | 97 +++ .../cybertriage/autopsy/ctapi/Constants.java | 83 +++ .../cybertriage/autopsy/ctapi/CtApiDAO.java | 83 +++ .../autopsy/ctapi/ProxySettings.java | 443 ++++++++++++++ .../autopsy/ctapi/json/AuthTokenRequest.java | 59 ++ .../autopsy/ctapi/json/AuthTokenResponse.java | 85 +++ .../ctapi/json/BoostLicenseResponse.java | 59 ++ .../autopsy/ctapi/json/CTScore.java | 77 +++ .../ctapi/json/DecryptedLicenseResponse.java | 87 +++ .../ctapi/json/FileReputationResult.java | 123 ++++ .../autopsy/ctapi/json/LicenseInfo.java | 45 ++ .../autopsy/ctapi/json/LicenseRequest.java | 59 ++ .../autopsy/ctapi/json/LicenseResponse.java | 59 ++ .../autopsy/ctapi/json/MetadataLabel.java | 43 ++ .../ctapi/util/CTHostIDGenerationUtil.java | 57 ++ .../ctapi/util/LicenseDecryptorUtil.java | 172 ++++++ .../autopsy/ctapi/util/Md5HashUtil.java | 40 ++ .../autopsy/ctapi/util/ObjectMapperUtil.java | 41 ++ .../autopsy/ctoptions/Bundle.properties | 5 + .../ctoptions/Bundle.properties-MERGED | 5 + .../autopsy/ctoptions/CTOptionsPanel.form | 39 ++ .../autopsy/ctoptions/CTOptionsPanel.java | 141 +++++ .../ctoptions/CTOptionsPanelController.java | 128 ++++ .../ctoptions/ctcloud/Bundle.properties | 23 + .../ctcloud/Bundle.properties-MERGED | 55 ++ .../ctoptions/ctcloud/CTLicenseDialog.form | 138 +++++ .../ctoptions/ctcloud/CTLicenseDialog.java | 192 ++++++ .../ctcloud/CTLicensePersistence.java | 90 +++ .../ctcloud/CTMalwareScannerOptionsPanel.form | 199 +++++++ .../ctcloud/CTMalwareScannerOptionsPanel.java | 551 ++++++++++++++++++ .../ctoptions/subpanel/CTOptionsSubPanel.java | 26 + .../df/cybertriage/autopsy/images/logo.png | Bin 0 -> 10482 bytes .../autopsy/malwarescan/BatchProcessor.java | 88 +++ .../malwarescan/Bundle.properties-MERGED | 23 + .../malwarescan/MalwareScanIngestModule.java | 395 +++++++++++++ .../MalwareScanIngestModuleFactory.java | 72 +++ CoreLibs/ivy.xml | 8 +- CoreLibs/manifest.mf | 2 +- CoreLibs/nbproject/project.properties | 1 + CoreLibs/nbproject/project.xml | 45 +- .../netbeans/core/startup/Bundle.properties | 4 +- .../core/windows/view/ui/Bundle.properties | 6 +- 45 files changed, 3959 insertions(+), 53 deletions(-) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.form create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.form create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/images/logo.png create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java diff --git a/Core/ivy.xml b/Core/ivy.xml index fba2d99acd..3d2352648d 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -1,3 +1,6 @@ + +]> @@ -72,6 +75,15 @@ + + + + + + + + + @@ -84,5 +96,6 @@ + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index a24bcb423e..9adb1c7649 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -18,6 +18,7 @@ file.reference.bcprov-jdk15on-1.70.jar=release/modules/ext/bcprov-jdk15on-1.70.j file.reference.bcutil-jdk15on-1.70.jar=release/modules/ext/bcutil-jdk15on-1.70.jar file.reference.c3p0-0.9.5.5.jar=release/modules/ext/c3p0-0.9.5.5.jar file.reference.checker-qual-3.33.0.jar=release/modules/ext/checker-qual-3.33.0.jar +file.reference.commons-codec-1.11.jar=release/modules/ext/commons-codec-1.11.jar file.reference.commons-dbcp2-2.9.0.jar=release/modules/ext/commons-dbcp2-2.9.0.jar file.reference.commons-io-2.11.0.jar=release/modules/ext/commons-io-2.11.0.jar file.reference.commons-lang3-3.10.jar=release/modules/ext/commons-lang3-3.10.jar @@ -31,6 +32,10 @@ file.reference.decodetect-core-0.3.jar=release/modules/ext/decodetect-core-0.3.j file.reference.error_prone_annotations-2.18.0.jar=release/modules/ext/error_prone_annotations-2.18.0.jar file.reference.failureaccess-1.0.1.jar=release/modules/ext/failureaccess-1.0.1.jar file.reference.guava-32.0.1-jre.jar=release/modules/ext/guava-32.0.1-jre.jar +file.reference.httpclient-4.5.14.jar=release/modules/ext/httpclient-4.5.14.jar +file.reference.httpclient-win-4.5.14.jar=release/modules/ext/httpclient-win-4.5.14.jar +file.reference.httpcore-4.4.16.jar=release/modules/ext/httpcore-4.4.16.jar +file.reference.httpmime-4.5.14.jar=release/modules/ext/httpmime-4.5.14.jar file.reference.icepdf-core-6.2.2.jar=release/modules/ext/icepdf-core-6.2.2.jar file.reference.icepdf-viewer-6.2.2.jar=release/modules/ext/icepdf-viewer-6.2.2.jar file.reference.istack-commons-runtime-3.0.11.jar=release/modules/ext/istack-commons-runtime-3.0.11.jar @@ -46,6 +51,7 @@ file.reference.javax.activation-api-1.2.0.jar=release/modules/ext/javax.activati file.reference.javax.ws.rs-api-2.1.1.jar=release/modules/ext/javax.ws.rs-api-2.1.1.jar file.reference.jaxb-api-2.3.1.jar=release/modules/ext/jaxb-api-2.3.1.jar file.reference.jaxb-runtime-2.3.3.jar=release/modules/ext/jaxb-runtime-2.3.3.jar +file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar file.reference.jfreechart-1.5.3.jar=release/modules/ext/jfreechart-1.5.3.jar file.reference.jgraphx-4.2.2.jar=release/modules/ext/jgraphx-4.2.2.jar @@ -55,6 +61,7 @@ file.reference.jutf7-1.0.0.jar=release/modules/ext/jutf7-1.0.0.jar file.reference.jxmapviewer2-2.6.jar=release/modules/ext/jxmapviewer2-2.6.jar file.reference.jython-standalone-2.7.2.jar=release/modules/ext/jython-standalone-2.7.2.jar file.reference.libphonenumber-8.12.45.jar=release/modules/ext/libphonenumber-8.12.45.jar +file.reference.license4j-runtime-library-4.7.1.jar=release/modules/ext/license4j-runtime-library-4.7.1.jar file.reference.listenablefuture-1.0.jar=release/modules/ext/listenablefuture-1.0.jar file.reference.logback-classic-1.2.10.jar=release/modules/ext/logback-classic-1.2.10.jar file.reference.logback-core-1.2.10.jar=release/modules/ext/logback-core-1.2.10.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 0553a915ca..2cab2a535f 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -66,6 +66,14 @@ + + org.netbeans.modules.keyring + + + + 1.41 + + org.netbeans.modules.options.api @@ -165,14 +173,6 @@ 9.29 - org.openide.filesystems.nb @@ -448,6 +448,10 @@ ext/checker-qual-3.33.0.jar release/modules/ext/checker-qual-3.33.0.jar + + ext/commons-codec-1.11.jar + release/modules/ext/commons-codec-1.11.jar + ext/commons-dbcp2-2.9.0.jar release/modules/ext/commons-dbcp2-2.9.0.jar @@ -500,6 +504,22 @@ ext/guava-32.0.1-jre.jar release/modules/ext/guava-32.0.1-jre.jar + + ext/httpclient-4.5.14.jar + release/modules/ext/httpclient-4.5.14.jar + + + ext/httpclient-win-4.5.14.jar + release/modules/ext/httpclient-win-4.5.14.jar + + + ext/httpcore-4.4.16.jar + release/modules/ext/httpcore-4.4.16.jar + + + ext/httpmime-4.5.14.jar + release/modules/ext/httpmime-4.5.14.jar + ext/icepdf-core-6.2.2.jar release/modules/ext/icepdf-core-6.2.2.jar @@ -560,6 +580,10 @@ ext/jaxb-runtime-2.3.3.jar release/modules/ext/jaxb-runtime-2.3.3.jar + + ext/jdom-2.0.5-contrib.jar + release/modules/ext/jdom-2.0.5-contrib.jar + ext/jdom-2.0.5.jar release/modules/ext/jdom-2.0.5.jar @@ -596,6 +620,10 @@ ext/libphonenumber-8.12.45.jar release/modules/ext/libphonenumber-8.12.45.jar + + ext/license4j-runtime-library-4.7.1.jar + release/modules/ext/license4j-runtime-library-4.7.1.jar + ext/listenablefuture-1.0.jar release/modules/ext/listenablefuture-1.0.jar diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java new file mode 100644 index 0000000000..8b0ff55ee5 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java @@ -0,0 +1,97 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi; + + +import java.util.Objects; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * + * @author rishwanth + */ + + +public class CTCloudException extends Exception{ + private final ErrorCode errorCode; + + public enum ErrorCode { + BAD_REQUEST("CT-400", "Unknown or Bad request. Please contact Basis support at " + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM + " for help diagnosing the problem."), + INVALID_KEY("CT-401", "An invalid license ID was used to access CyberTriage Cloud Service. Please contact Basis support " + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM + " for help diagnosing the problem."), + GATEWAY_TIMEOUT("CT-504", "Request to CyberTriage Cloud Service timed out. Please retry after some time. If issue persists, please contact Basis support at " + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM + " for assistance."), + UN_AUTHORIZED("CT-403", "An authorization error occurred. Please contact Basis support " + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM + " for help diagnosing the problem."), + PROXY_UNAUTHORIZED("CT-407", "Proxy authentication failed. Please validate the connection settings from the Options panel Proxy Settings."), + TEMP_UNAVAILABLE("CT-500", "CyberTriage Cloud Service temporarily unavailable; please try again later. If this problem persists, contact Basis support at " + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM), + UNKNOWN("CT-080", "Unknown error while communicating with CyberTriage Cloud Service. If this problem persists, contact Basis support at "+ Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM +" for assistance."), + UNKNOWN_HOST("CT-081", "Unknown host error. If this problem persists, contact Basis support at "+ Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM +" for assistance."), + NETWORK_ERROR("CT-015", "Error connecting to CyberTriage Cloud.\n" + + "Check your firewall or proxy settings.\n" + + "Contact Support (support@cybertriage.com) for further assistance"); + private final String errorcode; + private final String description; + + private ErrorCode(String errorcode, String description) { + this.errorcode = errorcode; + this.description = description; + } + + public String getCode() { + return errorcode; + } + + public String getDescription() { + return description; + } + + } + + public CTCloudException(CTCloudException.ErrorCode errorCode) { + super(errorCode.name()); + this.errorCode = errorCode; + } + + public CTCloudException(CTCloudException.ErrorCode errorCode, Throwable throwable) { + super(errorCode.name(), throwable); + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } + + public String getErrorDetails() { + if(getErrorCode() == CTCloudException.ErrorCode.UNKNOWN && Objects.nonNull(getCause())){ + return String.format("Malware scan error %s occurred. Please try \"Re Scan\" from the dashboard to attempt Malware scaning again. " + + "\nPlease contact Basis support at %s for help if the problem presists.", + StringUtils.isNotBlank(getCause().getLocalizedMessage()) ? "("+getCause().getLocalizedMessage()+")": "(Unknown)", + Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM ); + }else { + return getErrorCode().getDescription(); + } + } + + /* + * Attempts to find a more specific error code than "Unknown" for the given exception. + */ + public static ErrorCode parseUnknownException(Throwable throwable) { + + String stackTrace = ExceptionUtils.getStackTrace(throwable); + if (stackTrace.contains("UnknownHostException")) { + return ErrorCode.UNKNOWN_HOST; + } + + return ErrorCode.UNKNOWN; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java new file mode 100644 index 0000000000..3cc077c06d --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -0,0 +1,83 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2016 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi; + +import com.google.common.collect.ImmutableList; +import java.net.URI; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +// TODO take out anything sensitive or not used +final public class Constants { + + public static final String CYBER_TRIAGE = "CyberTriage"; + + public static final String IS_MEMORY_IMAGE = "IS_MEMORY_IMAGE"; + + + public static final String SSLTEST_URL = "https://www2.cybertriage.com/ssl_test.html"; + + + public static final String CT_CLOUD_SERVER = "https://rep1.cybertriage.com"; + + public static final String CT_CLOUD_DEV_SERVER = "https://cyber-triage-dev.appspot.com"; + + /** + * Link to watch demo video + * @since 3.1.0 + */ + public static final String DEMO_VIDEO_URL = "https://www.cybertriage.com/video/cyber-triage-demo-video/?utm_source=Cyber+Triage+Tool&utm_campaign=Eval+Demo+Video"; + + /** + * Link request quote + * @since 3.1.0 + */ + public static final String REQUEST_QUOTE_URL = "https://www.cybertriage.com/request-quote/?utm_source=Cyber+Triage+Tool&utm_campaign=Eval+Quote"; + + /** + * Latest help document URL + * @since 3.2.0 + */ + public static final URI USER_GUIDE_LATEST_URL = URI.create("https://docs.cybertriage.com/en/latest/?utm_source=Cyber+Triage+Tool&utm_campaign=Help+Docs"); + + /** + * Visit website URL + * @since 3.1.0 + */ + public static final String VISIT_WEBSITE_URL ="https://www.cybertriage.com/eval_data_202109/?utm_source=Cyber+Triage+Tool&utm_campaign=Eval+Data+Button"; + + + /** + * URL for visiting the website after the data is ingested on the dashboard. + */ + public static final String EVAL_WEBSITE_AUTO_URL = "https://www.cybertriage.com/eval_data_202109_auto/?utm_source=Cyber+Triage+Tool&utm_campaign=Eval+Data+Auto/"; //CT-4045 + + + public static final String SUPPORT_AT_CYBERTRIAGE_DOT_COM = "support@cybertriage.com"; + + public static final String SALES_AT_CYBERTRIAGE_DOT_COM = "sales@cybertriage.com"; + + public final static String AUTODETECT = "Auto Detect"; + + public final static int RESTAPI_PORT = 9443; + + public static final String INVALID_HOSTNAME_REQUEST = "Request rejected. Invalid host name. Hostname contains characters that are not allowed. \n" + + "Characters that are not allowed include `~!@#$&^*(){}[]\\\\|;'\",<>/? \n" + + "You may input the host IP address if the name is not resolving."; + public static final String INVALID_HOSTNAME_UI = "Invalid host name. Hostname contains characters that are not allowed. \n" + + "Characters that are not allowed include `~!@#$&^*(){}[]\\\\|;'\",<>/?"; + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java new file mode 100644 index 0000000000..66fa03b8f1 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -0,0 +1,83 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi; + +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest; +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import org.sleuthkit.autopsy.coreutils.Version; + +/** + * + * Data access layer for handling the CT api. + */ +public class CtApiDAO { + + private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate"; + private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token"; + + private static final CtApiDAO instance = new CtApiDAO(); + private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper(); + + private CtApiDAO() { + } + + public static CtApiDAO getInstance() { + return instance; + } + + private static String getAppVersion() { + return Version.getName() + " " + Version.getVersion(); + } + + private T doPost(String urlPath, Object requestBody, Class responseTypeRef) throws CTCloudException { + return null; + // TODO + } + + public LicenseResponse getLicenseInfo(String licenseString) throws CTCloudException { + LicenseRequest licenseRequest = new LicenseRequest() + .setBoostLicenseCode(licenseString) + .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) + .setProduct(getAppVersion()); + + return doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); + + } + + public AuthTokenResponse getAuthToken(String boostLicenseId) throws CTCloudException { + AuthTokenRequest authTokenRequest = new AuthTokenRequest() + .setAutopsyVersion(getAppVersion()) + .setRequestFileUpload(true) + .setBoostLicenseId(boostLicenseId); + + return doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); + } + + public List getReputationResults(String authToken, List md5Hashes) throws CTCloudException { + // TODO +// return cloudServiceApi.lookupFileResults(md5Hashes, HashTypes.md5); + return null; + } + + public enum ResultType { + OK, SERVER_ERROR, NOT_AUTHORIZED + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java new file mode 100644 index 0000000000..d341b99e57 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java @@ -0,0 +1,443 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi; + + + +import java.net.*; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; +import org.netbeans.api.keyring.Keyring; +import org.openide.util.*; +import org.openide.util.lookup.ServiceProvider; + +/** + * Taken from https://raw.githubusercontent.com/apache/netbeans/master/platform/o.n.core/src/org/netbeans/core/ProxySettings.java + * @author Jiri Rechtacek + */ +public class ProxySettings { + + public static final String PROXY_HTTP_HOST = "proxyHttpHost"; // NOI18N + public static final String PROXY_HTTP_PORT = "proxyHttpPort"; // NOI18N + public static final String PROXY_HTTPS_HOST = "proxyHttpsHost"; // NOI18N + public static final String PROXY_HTTPS_PORT = "proxyHttpsPort"; // NOI18N + public static final String PROXY_SOCKS_HOST = "proxySocksHost"; // NOI18N + public static final String PROXY_SOCKS_PORT = "proxySocksPort"; // NOI18N + public static final String NOT_PROXY_HOSTS = "proxyNonProxyHosts"; // NOI18N + public static final String PROXY_TYPE = "proxyType"; // NOI18N + public static final String USE_PROXY_AUTHENTICATION = "useProxyAuthentication"; // NOI18N + public static final String PROXY_AUTHENTICATION_USERNAME = "proxyAuthenticationUsername"; // NOI18N + public static final String PROXY_AUTHENTICATION_PASSWORD = "proxyAuthenticationPassword"; // NOI18N + public static final String USE_PROXY_ALL_PROTOCOLS = "useProxyAllProtocols"; // NOI18N + public static final String DIRECT = "DIRECT"; // NOI18N + public static final String PAC = "PAC"; // NOI18N + + public static final String SYSTEM_PROXY_HTTP_HOST = "systemProxyHttpHost"; // NOI18N + public static final String SYSTEM_PROXY_HTTP_PORT = "systemProxyHttpPort"; // NOI18N + public static final String SYSTEM_PROXY_HTTPS_HOST = "systemProxyHttpsHost"; // NOI18N + public static final String SYSTEM_PROXY_HTTPS_PORT = "systemProxyHttpsPort"; // NOI18N + public static final String SYSTEM_PROXY_SOCKS_HOST = "systemProxySocksHost"; // NOI18N + public static final String SYSTEM_PROXY_SOCKS_PORT = "systemProxySocksPort"; // NOI18N + public static final String SYSTEM_NON_PROXY_HOSTS = "systemProxyNonProxyHosts"; // NOI18N + public static final String SYSTEM_PAC = "systemPAC"; // NOI18N + + // Only for testing purpose (Test connection in General options panel) + public static final String TEST_SYSTEM_PROXY_HTTP_HOST = "testSystemProxyHttpHost"; // NOI18N + public static final String TEST_SYSTEM_PROXY_HTTP_PORT = "testSystemProxyHttpPort"; // NOI18N + public static final String HTTP_CONNECTION_TEST_URL = "https://netbeans.apache.org";// NOI18N + + private static String presetNonProxyHosts; + + /** No proxy is used to connect. */ + public static final int DIRECT_CONNECTION = 0; + + /** Proxy setting is automatically detect in OS. */ + public static final int AUTO_DETECT_PROXY = 1; // as default + + /** Manually set proxy host and port. */ + public static final int MANUAL_SET_PROXY = 2; + + /** Proxy PAC file automatically detect in OS. */ + public static final int AUTO_DETECT_PAC = 3; + + /** Proxy PAC file manually set. */ + public static final int MANUAL_SET_PAC = 4; + + private static final Logger LOGGER = Logger.getLogger(ProxySettings.class.getName()); + + private static Preferences getPreferences() { + return NbPreferences.forModule (ProxySettings.class); + } + + + public static String getHttpHost () { + return normalizeProxyHost (getPreferences ().get (PROXY_HTTP_HOST, "")); + } + + public static String getHttpPort () { + return getPreferences ().get (PROXY_HTTP_PORT, ""); + } + + public static String getHttpsHost () { + if (useProxyAllProtocols ()) { + return getHttpHost (); + } else { + return getPreferences ().get (PROXY_HTTPS_HOST, ""); + } + } + + public static String getHttpsPort () { + if (useProxyAllProtocols ()) { + return getHttpPort (); + } else { + return getPreferences ().get (PROXY_HTTPS_PORT, ""); + } + } + + public static String getSocksHost () { + if (useProxyAllProtocols ()) { + return getHttpHost (); + } else { + return getPreferences ().get (PROXY_SOCKS_HOST, ""); + } + } + + public static String getSocksPort () { + if (useProxyAllProtocols ()) { + return getHttpPort (); + } else { + return getPreferences ().get (PROXY_SOCKS_PORT, ""); + } + } + + public static String getNonProxyHosts () { + String hosts = getPreferences ().get (NOT_PROXY_HOSTS, getDefaultUserNonProxyHosts ()); + return compactNonProxyHosts(hosts); + } + + public static int getProxyType () { + int type = getPreferences ().getInt (PROXY_TYPE, AUTO_DETECT_PROXY); + if (AUTO_DETECT_PROXY == type) { + type = ProxySettings.getSystemPac() != null ? AUTO_DETECT_PAC : AUTO_DETECT_PROXY; + } + return type; + } + + + public static String getSystemHttpHost() { + return getPreferences().get(SYSTEM_PROXY_HTTP_HOST, ""); + } + + public static String getSystemHttpPort() { + return getPreferences().get(SYSTEM_PROXY_HTTP_PORT, ""); + } + + public static String getSystemHttpsHost() { + return getPreferences().get(SYSTEM_PROXY_HTTPS_HOST, ""); + } + + public static String getSystemHttpsPort() { + return getPreferences().get(SYSTEM_PROXY_HTTPS_PORT, ""); + } + + public static String getSystemSocksHost() { + return getPreferences().get(SYSTEM_PROXY_SOCKS_HOST, ""); + } + + public static String getSystemSocksPort() { + return getPreferences().get(SYSTEM_PROXY_SOCKS_PORT, ""); + } + + public static String getSystemNonProxyHosts() { + return getPreferences().get(SYSTEM_NON_PROXY_HOSTS, getModifiedNonProxyHosts("")); + } + + public static String getSystemPac() { + return getPreferences().get(SYSTEM_PAC, null); + } + + + public static String getTestSystemHttpHost() { + return getPreferences().get(TEST_SYSTEM_PROXY_HTTP_HOST, ""); + } + + public static String getTestSystemHttpPort() { + return getPreferences().get(TEST_SYSTEM_PROXY_HTTP_PORT, ""); + } + + + public static boolean useAuthentication () { + return getPreferences ().getBoolean (USE_PROXY_AUTHENTICATION, false); + } + + public static boolean useProxyAllProtocols () { + return getPreferences ().getBoolean (USE_PROXY_ALL_PROTOCOLS, false); + } + + public static String getAuthenticationUsername () { + return getPreferences ().get (PROXY_AUTHENTICATION_USERNAME, ""); + } + + public static char[] getAuthenticationPassword () { + String old = getPreferences().get(PROXY_AUTHENTICATION_PASSWORD, null); + if (old != null) { + getPreferences().remove(PROXY_AUTHENTICATION_PASSWORD); + setAuthenticationPassword(old.toCharArray()); + } + char[] pwd = Keyring.read(PROXY_AUTHENTICATION_PASSWORD); + return pwd != null ? pwd : new char[0]; + } + + public static void setAuthenticationPassword(char[] password) { + Keyring.save(ProxySettings.PROXY_AUTHENTICATION_PASSWORD, password, + // XXX consider including getHttpHost and/or getHttpsHost + NbBundle.getMessage(ProxySettings.class, "ProxySettings.password.description")); // NOI18N + } + + public static void addPreferenceChangeListener (PreferenceChangeListener l) { + getPreferences ().addPreferenceChangeListener (l); + } + + public static void removePreferenceChangeListener (PreferenceChangeListener l) { + getPreferences ().removePreferenceChangeListener (l); + } + + private static String getPresetNonProxyHosts () { + if (presetNonProxyHosts == null) { + presetNonProxyHosts = System.getProperty ("http.nonProxyHosts", ""); // NOI18N + } + return presetNonProxyHosts; + } + + private static String getDefaultUserNonProxyHosts () { + return getModifiedNonProxyHosts (getSystemNonProxyHosts ()); + } + + + private static String concatProxies(String... proxies) { + StringBuilder sb = new StringBuilder(); + for (String n : proxies) { + if (n == null) { + continue; + } + n = n.trim(); + if (n.isEmpty()) { + continue; + } + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '|') { // NOI18N + if (!n.startsWith("|")) { // NOI18N + sb.append('|'); // NOI18N + } + } + sb.append(n); + } + return sb.toString(); + } + + private static String getModifiedNonProxyHosts (String systemPreset) { + String fromSystem = systemPreset.replace (";", "|").replace (",", "|"); //NOI18N + String fromUser = getPresetNonProxyHosts () == null ? "" : getPresetNonProxyHosts ().replace (";", "|").replace (",", "|"); //NOI18N + if (Utilities.isWindows ()) { + fromSystem = addReguralToNonProxyHosts (fromSystem); + } + final String staticNonProxyHosts = NbBundle.getMessage(ProxySettings.class, "StaticNonProxyHosts"); // NOI18N + String nonProxy = concatProxies(fromUser, fromSystem, staticNonProxyHosts); // NOI18N + String localhost; + try { + localhost = InetAddress.getLocalHost().getHostName(); + if (!"localhost".equals(localhost)) { // NOI18N + nonProxy = nonProxy + "|" + localhost; // NOI18N + } else { + // Avoid this error when hostname == localhost: + // Error in http.nonProxyHosts system property: sun.misc.REException: localhost is a duplicate + } + } + catch (UnknownHostException e) { + // OK. Sometimes a hostname is assigned by DNS, but a computer + // is later pulled off the network. It may then produce a bogus + // name for itself which can't actually be resolved. Normally + // "localhost" is aliased to 127.0.0.1 anyway. + } + /* per Milan's agreement it's removed. See issue #89868 + try { + String localhost2 = InetAddress.getLocalHost().getCanonicalHostName(); + if (!"localhost".equals(localhost2) && !localhost2.equals(localhost)) { // NOI18N + nonProxy = nonProxy + "|" + localhost2; // NOI18N + } else { + // Avoid this error when hostname == localhost: + // Error in http.nonProxyHosts system property: sun.misc.REException: localhost is a duplicate + } + } + catch (UnknownHostException e) { + // OK. Sometimes a hostname is assigned by DNS, but a computer + // is later pulled off the network. It may then produce a bogus + // name for itself which can't actually be resolved. Normally + // "localhost" is aliased to 127.0.0.1 anyway. + } + */ + return compactNonProxyHosts (nonProxy); + } + + + // avoid duplicate hosts + private static String compactNonProxyHosts (String hosts) { + StringTokenizer st = new StringTokenizer(hosts, ","); //NOI18N + StringBuilder nonProxyHosts = new StringBuilder(); + while (st.hasMoreTokens()) { + String h = st.nextToken().trim(); + if (h.length() == 0) { + continue; + } + if (nonProxyHosts.length() > 0) { + nonProxyHosts.append("|"); // NOI18N + } + nonProxyHosts.append(h); + } + st = new StringTokenizer (nonProxyHosts.toString(), "|"); //NOI18N + Set set = new HashSet (); + StringBuilder compactedProxyHosts = new StringBuilder(); + while (st.hasMoreTokens ()) { + String t = st.nextToken (); + if (set.add (t.toLowerCase (Locale.US))) { + if (compactedProxyHosts.length() > 0) { + compactedProxyHosts.append('|'); // NOI18N + } + compactedProxyHosts.append(t); + } + } + return compactedProxyHosts.toString(); + } + + private static String addReguralToNonProxyHosts (String nonProxyHost) { + StringTokenizer st = new StringTokenizer (nonProxyHost, "|"); // NOI18N + StringBuilder reguralProxyHosts = new StringBuilder(); + while (st.hasMoreTokens ()) { + String t = st.nextToken (); + if (t.indexOf ('*') == -1) { //NOI18N + t = t + '*'; //NOI18N + } + if (reguralProxyHosts.length() > 0) + reguralProxyHosts.append('|'); // NOI18N + reguralProxyHosts.append(t); + } + + return reguralProxyHosts.toString(); + } + + public static String normalizeProxyHost (String proxyHost) { + if (proxyHost.toLowerCase (Locale.US).startsWith ("http://")) { // NOI18N + return proxyHost.substring (7, proxyHost.length ()); + } else { + return proxyHost; + } + } + + private static InetSocketAddress analyzeProxy(URI uri) { + Parameters.notNull("uri", uri); // NOI18N + List proxies = ProxySelector.getDefault().select(uri); + assert proxies != null : "ProxySelector cannot return null for " + uri; // NOI18N + assert !proxies.isEmpty() : "ProxySelector cannot return empty list for " + uri; // NOI18N + String protocol = uri.getScheme(); + Proxy p = proxies.get(0); + if (Proxy.Type.DIRECT == p.type()) { + // return null for DIRECT proxy + return null; + } + if (protocol == null + || ((protocol.startsWith("http") || protocol.equals("ftp")) && Proxy.Type.HTTP == p.type()) // NOI18N + || !(protocol.startsWith("http") || protocol.equals("ftp"))) { // NOI18N + if (p.address() instanceof InetSocketAddress) { + // check is + //assert ! ((InetSocketAddress) p.address()).isUnresolved() : p.address() + " must be resolved address."; + return (InetSocketAddress) p.address(); + } else { + LOGGER.log(Level.INFO, p.address() + " is not instanceof InetSocketAddress but " + p.address().getClass()); // NOI18N + return null; + } + } else { + return null; + } + } + + public static void reload() { + Reloader reloader = Lookup.getDefault().lookup(Reloader.class); + reloader.reload(); + } + + @ServiceProvider(service = NetworkSettings.ProxyCredentialsProvider.class, position = 1000) + public static class NbProxyCredentialsProvider extends NetworkSettings.ProxyCredentialsProvider { + + @Override + public String getProxyHost(URI u) { + if (getPreferences() == null) { + return null; + } + InetSocketAddress sa = analyzeProxy(u); + return sa == null ? null : sa.getHostName(); + } + + @Override + public String getProxyPort(URI u) { + if (getPreferences() == null) { + return null; + } + InetSocketAddress sa = analyzeProxy(u); + return sa == null ? null : Integer.toString(sa.getPort()); + } + + @Override + protected String getProxyUserName(URI u) { + if (getPreferences() == null) { + return null; + } + return ProxySettings.getAuthenticationUsername(); + } + + @Override + protected char[] getProxyPassword(URI u) { + if (getPreferences() == null) { + return null; + } + return ProxySettings.getAuthenticationPassword(); + } + + @Override + protected boolean isProxyAuthentication(URI u) { + if (getPreferences() == null) { + return false; + } + return getPreferences().getBoolean(USE_PROXY_AUTHENTICATION, false); + } + + } + + /** A bridge between o.n.core and core.network. + * An implementation of this class brings a facility to reload Network Proxy Settings + * from underlying OS. + * The module core.network provides a implementation which may be accessible + * via Lookup.getDefault(). It's not guaranteed any implementation is found on all distribution. + * + * @since 3.40 + */ + public abstract static class Reloader { + + /** Reloads Network Proxy Settings from underlying system. + * + */ + public abstract void reload(); + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java new file mode 100644 index 0000000000..6cda146c84 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java @@ -0,0 +1,59 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * POJO for an auth token request. + */ +public class AuthTokenRequest { + + @JsonProperty("autopsy_version") + private String autopsyVersion; + + @JsonProperty("boost_license_id") + private String boostLicenseId; + + @JsonProperty("requestFileUpload") + private boolean requestFileUpload; + + public String getAutopsyVersion() { + return autopsyVersion; + } + + public AuthTokenRequest setAutopsyVersion(String autopsyVersion) { + this.autopsyVersion = autopsyVersion; + return this; + } + + public String getBoostLicenseId() { + return boostLicenseId; + } + + public AuthTokenRequest setBoostLicenseId(String boostLicenseId) { + this.boostLicenseId = boostLicenseId; + return this; + } + + public boolean isRequestFileUpload() { + return requestFileUpload; + } + + public AuthTokenRequest setRequestFileUpload(boolean requestFileUpload) { + this.requestFileUpload = requestFileUpload; + return this; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java new file mode 100644 index 0000000000..a010bbe13c --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -0,0 +1,85 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; + +/** + * POJO for an auth token response. + */ +public class AuthTokenResponse { + private final String token; + private final String apiKey; + private final Long hashLookupCount; + private final Long hashLookupLimit; + private final Long fileUploadLimit; + private final Long fileUploadCount; + private final String fileUploadUrl; + private final ZonedDateTime expiration; + + @JsonCreator + public AuthTokenResponse( + @JsonProperty("token") String token, + @JsonProperty("api_key") String apiKey, + @JsonProperty("hashLookupCount") Long hashLookupCount, + @JsonProperty("hashLookupLimit") Long hashLookupLimit, + @JsonProperty("fileUploadLimit") Long fileUploadLimit, + @JsonProperty("fileUploadCount") Long fileUploadCount, + @JsonProperty("fileUploadUrl") String fileUploadUrl, + @JsonProperty("expiration") ZonedDateTime expiration + ) { + this.token = token; + this.apiKey = apiKey; + this.hashLookupCount = hashLookupCount; + this.hashLookupLimit = hashLookupLimit; + this.fileUploadLimit = fileUploadLimit; + this.fileUploadCount = fileUploadCount; + this.fileUploadUrl = fileUploadUrl; + this.expiration = expiration; + } + + public String getToken() { + return token; + } + + public String getApiKey() { + return apiKey; + } + + public Long getHashLookupCount() { + return hashLookupCount; + } + + public Long getHashLookupLimit() { + return hashLookupLimit; + } + + public Long getFileUploadLimit() { + return fileUploadLimit; + } + + public Long getFileUploadCount() { + return fileUploadCount; + } + + public String getFileUploadUrl() { + return fileUploadUrl; + } + + public ZonedDateTime getExpiration() { + return expiration; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java new file mode 100644 index 0000000000..ce213aeba4 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java @@ -0,0 +1,59 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * POJO for a boost license response object that is a part of the license + * response. + */ +public class BoostLicenseResponse { + + private final String version; + private final String iv; + private final String encryptedKey; + private final String encryptedJson; + + @JsonCreator + public BoostLicenseResponse( + @JsonProperty("version") String version, + @JsonProperty("iv") String iv, + @JsonProperty("encryptedKey") String encryptedKey, + @JsonProperty("encryptedJson") String encryptedJson) { + + this.version = version; + this.iv = iv; + this.encryptedKey = encryptedKey; + this.encryptedJson = encryptedJson; + } + + public String getVersion() { + return version; + } + + public String getIv() { + return iv; + } + + public String getEncryptedKey() { + return encryptedKey; + } + + public String getEncryptedJson() { + return encryptedJson; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java new file mode 100644 index 0000000000..fbb5665fba --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java @@ -0,0 +1,77 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2014 - 2016 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.google.common.base.MoreObjects; +import static com.google.common.base.Preconditions.checkArgument; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.Score.Priority; +import org.sleuthkit.datamodel.Score.Significance; + +/** + * + * Score class represents a conclusion and the relative confidence in the conclusion about + * a subject. A subject may be an Item, a category/analysis result etc. + * @since 1.7.0 + * + */ +public enum CTScore { + + /* + Enum names without method defaults to AUTO + NOTABLE -> NOTABLE + */ + + // Unknown None + UNKNOWN(new Score(Significance.UNKNOWN, Priority.NORMAL)), + // GOOD_MEDIUM + LIKELY_NONE(new Score(Significance.LIKELY_NONE, Priority.NORMAL)), + // SUSPICIOUS_HIGH / BAD_MEDIUM + LIKELY_NOTABLE(new Score(Significance.LIKELY_NOTABLE, Priority.NORMAL)), + // GOOD_HIGH + NONE(new Score(Significance.NONE, Priority.NORMAL)), + // BAD_HIGH + NOTABLE(new Score(Significance.NOTABLE, Priority.NORMAL)), + // SUSPICIOUS (User flagged) + LIKELY_NOTABLE_MANUAL(new Score(Significance.LIKELY_NOTABLE, Priority.OVERRIDE)), + // Good (User flagged) + NONE_MANUAL(new Score(Significance.NONE, Priority.OVERRIDE)), + // Bad (User flagged) + NOTABLE_MANUAL(new Score(Significance.NOTABLE, Priority.OVERRIDE)); + + + private final Score tskScore; + + /** + * Create a CTScore instance based on score + * @param tskScore + */ + private CTScore(Score tskScore) { + + checkArgument(tskScore.getSignificance() == Significance.UNKNOWN ? tskScore.getPriority() == Priority.NORMAL : true, "Unknown Conclusions expects no (NORMAL) priority"); + this.tskScore = tskScore; + } + + public Score getTskCore() { + return tskScore; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("Method Category", tskScore.getPriority()) + .add("Significance", tskScore.getSignificance()).toString(); + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java new file mode 100644 index 0000000000..ec9e74a2ed --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -0,0 +1,87 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; + +/** + * POJO for after encrypted boost license has been decrypted. + */ +public class DecryptedLicenseResponse { + + private final String boostLicenseId; + private final String licenseHostId; + private final ZonedDateTime expirationDate; + private final Long hashLookups; + private final Long fileUploads; + private final ZonedDateTime activationTime; + private final String product; + private final String limitType; + + @JsonCreator + public DecryptedLicenseResponse( + @JsonProperty("boostLicenseId") String boostLicenseId, + @JsonProperty("licenseHostId") String licenseHostId, + @JsonProperty("expirationDate") ZonedDateTime expirationDate, + @JsonProperty("hashLookups") Long hashLookups, + @JsonProperty("fileUploads") Long fileUploads, + @JsonProperty("activationTime") ZonedDateTime activationTime, + @JsonProperty("product") String product, + @JsonProperty("limitType") String limitType + ) { + this.boostLicenseId = boostLicenseId; + this.licenseHostId = licenseHostId; + this.expirationDate = expirationDate; + this.hashLookups = hashLookups; + this.fileUploads = fileUploads; + this.activationTime = activationTime; + this.product = product; + this.limitType = limitType; + } + + public String getBoostLicenseId() { + return boostLicenseId; + } + + public String getLicenseHostId() { + return licenseHostId; + } + + public Long getHashLookups() { + return hashLookups; + } + + public Long getFileUploads() { + return fileUploads; + } + + public ZonedDateTime getActivationTime() { + return activationTime; + } + + public String getProduct() { + return product; + } + + public String getLimitType() { + return limitType; + } + + public ZonedDateTime getExpirationDate() { + return expirationDate; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java new file mode 100644 index 0000000000..53e604e381 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java @@ -0,0 +1,123 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import java.util.List; + +/** + * A file reputation result regarding malware status. + */ +public class FileReputationResult { + + public static enum Status { + FOUND, + NOT_FOUND, + ERROR, + LIMITS_EXCEEDED, + BEING_SCANNED; + } + + public enum CorrelationFrequency { + UNIQUE, + RARE, + COMMON; + } + + private final String malwareDescription; + private final Status status; + private final CTScore score; + private final String md5Hash; + private final String sha1Hash; + private final ZonedDateTime firstScanDate; + private final ZonedDateTime lastScanDate; + private final List metadata; + private final String statusDescription; + private final CorrelationFrequency frequency; + private final String frequencyDescription; + + @JsonCreator + public FileReputationResult( + @JsonProperty("malwareDescription") String malwareDescription, + @JsonProperty("status") Status status, + @JsonProperty("score") CTScore score, + @JsonProperty("md5Hash") String md5Hash, + @JsonProperty("sha1Hash") String sha1Hash, + @JsonProperty("firstScanDate") ZonedDateTime firstScanDate, + @JsonProperty("lastScanDate") ZonedDateTime lastScanDate, + @JsonProperty("metadata") List metadata, + @JsonProperty("statusDescription") String statusDescription, + @JsonProperty("frequency") CorrelationFrequency frequency, + @JsonProperty("frequencyDescription") String frequencyDescription + ) { + this.malwareDescription = malwareDescription; + this.status = status; + this.score = score; + this.md5Hash = md5Hash; + this.sha1Hash = sha1Hash; + this.firstScanDate = firstScanDate; + this.lastScanDate = lastScanDate; + this.metadata = metadata; + this.statusDescription = statusDescription; + this.frequency = frequency; + this.frequencyDescription = frequencyDescription; + } + + public String getMalwareDescription() { + return malwareDescription; + } + + public Status getStatus() { + return status; + } + + public CTScore getScore() { + return score; + } + + public String getMd5Hash() { + return md5Hash; + } + + public String getSha1Hash() { + return sha1Hash; + } + + public ZonedDateTime getFirstScanDate() { + return firstScanDate; + } + + public ZonedDateTime getLastScanDate() { + return lastScanDate; + } + + public List getMetadata() { + return metadata; + } + + public String getStatusDescription() { + return statusDescription; + } + + public CorrelationFrequency getFrequency() { + return frequency; + } + + public String getFrequencyDescription() { + return frequencyDescription; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java new file mode 100644 index 0000000000..8045686133 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java @@ -0,0 +1,45 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +/** + * Contains license info and decrypted boost license. + */ +public class LicenseInfo { + private final LicenseResponse licenseResponse; + private final DecryptedLicenseResponse decryptedLicense; + + public LicenseInfo(LicenseResponse licenseResponse, DecryptedLicenseResponse decryptedLicense) { + this.licenseResponse = licenseResponse; + this.decryptedLicense = decryptedLicense; + } + + public LicenseResponse getLicenseResponse() { + return licenseResponse; + } + + public DecryptedLicenseResponse getDecryptedLicense() { + return decryptedLicense; + } + + // TODO + public String getUser() { + return "TBD"; + } + + // TODO + public String getEmail() { + return "TBD"; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java new file mode 100644 index 0000000000..c87878596f --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java @@ -0,0 +1,59 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * POJO for license request information. + */ +public class LicenseRequest { + @JsonProperty("host_id") + private String hostId; + + @JsonProperty("boost_license_code") + private String boostLicenseCode; + + @JsonProperty("product") + private String product; + + public String getHostId() { + return hostId; + } + + public LicenseRequest setHostId(String hostId) { + this.hostId = hostId; + return this; + } + + public String getBoostLicenseCode() { + return boostLicenseCode; + } + + public LicenseRequest setBoostLicenseCode(String boostLicenseCode) { + this.boostLicenseCode = boostLicenseCode; + return this; + } + + public String getProduct() { + return product; + } + + public LicenseRequest setProduct(String product) { + this.product = product; + return this; + } + + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java new file mode 100644 index 0000000000..39184322e2 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java @@ -0,0 +1,59 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Response POJO for request for license. + */ +public class LicenseResponse { + private final Boolean success; + private final Boolean hostChanged; + private final Long hostChangesRemaining; + private final BoostLicenseResponse boostLicense; + + @JsonCreator + public LicenseResponse( + @JsonProperty("success") Boolean success, + @JsonProperty("hostChanged") Boolean hostChanged, + @JsonProperty("hostChangesRemaining") Long hostChangesRemaining, + @JsonProperty("boostLicense") BoostLicenseResponse boostLicense + ) { + this.success = success; + this.hostChanged = hostChanged; + this.hostChangesRemaining = hostChangesRemaining; + this.boostLicense = boostLicense; + } + + public Boolean isSuccess() { + return success; + } + + public Boolean isHostChanged() { + return hostChanged; + } + + public Long getHostChangesRemaining() { + return hostChangesRemaining; + } + + public BoostLicenseResponse getBoostLicense() { + return boostLicense; + } + + + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java new file mode 100644 index 0000000000..8077250c8b --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java @@ -0,0 +1,43 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author gregd + */ +public class MetadataLabel { + + private final String key; + private final String value; + private final String extendedInfo; + + @JsonCreator + public MetadataLabel( + @JsonProperty("key") String key, + @JsonProperty("value") String value, + @JsonProperty("info") String extendedInfo + ) { + this.key = key; + this.value = value; + this.extendedInfo = extendedInfo; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public String getExtendedInfo() { + return extendedInfo; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java new file mode 100644 index 0000000000..f7b68f6bea --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java @@ -0,0 +1,57 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2021 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.util; + +import com.license4j.HardwareID; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.logging.Level; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Utility class to generate license hostID and Target hostID for malware scan + * + * @author rishwanth + */ +public class CTHostIDGenerationUtil { + + private static final Logger LOGGER = Logger.getLogger(CTHostIDGenerationUtil.class.getName()); + private static final String USER_NAME = System.getProperty("user.name"); + private static String HOST_NAME = ""; + + /** + * Host ID Algorithm: Get MAC address from License4J. Get MD5 hash of it and + * grab the first 16 characters of the hash. Get user name that Cyber Triage + * is running as. MD5 hash of user name. Grab first 16 characters. + * Concatenate them and separate with underscore. Example: + * c84f70d1baf96420_7d7519bf21602c24 + * + * @return + */ + public static String generateLicenseHostID() { + if (StringUtils.isBlank(HOST_NAME)) { + + try { + HOST_NAME = StringUtils.defaultString(InetAddress.getLocalHost().getCanonicalHostName()); + } catch (UnknownHostException ex) { + LOGGER.log(Level.WARNING, "UNable to determine host name.", ex); + } + } + String macAddressMd5 = StringUtils.isNotBlank(HardwareID.getHardwareIDFromEthernetAddress()) ? Md5HashUtil.getMD5MessageDigest(HardwareID.getHardwareIDFromEthernetAddress()).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16); + String usernameMd5 = StringUtils.isNotBlank(USER_NAME) ? Md5HashUtil.getMD5MessageDigest(USER_NAME).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16); + String md5 = macAddressMd5 + "_" + usernameMd5; + return md5; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java new file mode 100644 index 0000000000..efa91afd8d --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java @@ -0,0 +1,172 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.util; + +import com.basistech.df.cybertriage.autopsy.ctapi.json.BoostLicenseResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Decrypts the payload of boost license. + */ +public class LicenseDecryptorUtil { + + private static final LicenseDecryptorUtil instance = new LicenseDecryptorUtil(); + + public static LicenseDecryptorUtil getInstance() { + return instance; + } + + private final ObjectMapper objectMapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper(); + + private LicenseDecryptorUtil() { + } + + public LicenseInfo createLicenseInfo(LicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException { + if (licenseResponse == null || licenseResponse.getBoostLicense() == null) { + throw new InvalidLicenseException("License or boost license are null"); + } + + DecryptedLicenseResponse decrypted = parseLicenseJSON(licenseResponse.getBoostLicense()); + return new LicenseInfo(licenseResponse, decrypted); + } + + /** + * Decrypts a boost license response. + * + * @param licenseResponse The boost license response. + * @return The decrypted license response. + * @throws JsonProcessingException + * @throws + * com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException + */ + public DecryptedLicenseResponse parseLicenseJSON(BoostLicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException { + + String decryptedJsonResponse; + try { + decryptedJsonResponse = decryptLicenseString( + licenseResponse.getEncryptedJson(), + licenseResponse.getIv(), + licenseResponse.getEncryptedKey(), + licenseResponse.getVersion() + ); + } catch (IOException | GeneralSecurityException ex) { + throw new InvalidLicenseException("An exception occurred while parsing the license string", ex); + } + + DecryptedLicenseResponse decryptedLicense = objectMapper.readValue(decryptedJsonResponse, DecryptedLicenseResponse.class); + if (!"CYBERTRIAGE".equalsIgnoreCase(decryptedLicense.getProduct())) { + // license file is expected to contain product of "CYBERTRIAGE" + throw new InvalidLicenseException("Not a valid Cyber Triage license"); + } + + return decryptedLicense; + } + + private String decryptLicenseString(String encryptedJson, String ivBase64, String encryptedKey, String version) throws IOException, GeneralSecurityException, InvalidLicenseException { + if (!"1.0".equals(version)) { + throw new InvalidLicenseException("Unexpected file version: " + version); + } + + byte[] encryptedKeyBytes = Base64.getDecoder().decode(encryptedKey); + byte[] keyBytes = decryptKey(encryptedKeyBytes); + SecretKey key = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"); + + byte[] ivBytes = Base64.getDecoder().decode(ivBase64); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + + byte[] encryptedLicenseJsonBytes = Base64.getDecoder().decode(encryptedJson); + + String algorithm = "AES/CBC/PKCS5Padding"; + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + byte[] licenseJsonBytes = cipher.doFinal(encryptedLicenseJsonBytes); + + return new String(licenseJsonBytes, StandardCharsets.UTF_8); + } + + private PublicKey getPublicKey() throws InvalidKeySpecException, NoSuchAlgorithmException { + + String publicKeyString = """ + -----BEGIN PUBLIC KEY----- + MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwIKulLyaLQ2WeO0gIW2G + 3jQqny3Y/7VUevBKulAEywaUbvECvZ4zGsnaMyACjXxMNkA1xU2WeSMP/WqC03wz + 4d71liUeAqOYKMdGHXFN2qswWz/ufK6An0pTEqYaoiUfcwSBVo2ZTUcMQexScKaS + ghmaWqBHBYx+lBkVMcLG2PtLDRZbqgJvJr2QCzMSVUpEGGQEWs7YolIq46KCgqsq + pTdfrdqd59x6oRhTLegswzxwLyouvrKbRqKR2ZRbVvlGtUnnnlLDuhEfd0flMxuv + W98Siw6dWe1K3x45nDu5py2G9Q9fZS8/2KHUC6QcLLstLIoPnZjCl9Lcur1U6s9N + f5aLI9mwMfmSJsoVOuwx2/MC98uHvPoPbG4ZjiT0aaGg4JccTGD6pssDA35zPhkk + 1l6wktEYtyF2A7zjzuFxioQz8fHBzIbHPCxzu4S2gh3qOVFf7c9COmX9MsnB70o2 + EZ1rxlFIJ7937IGJNwWOQuiMKTpEeT6BwTdQNZQPqCUGvZ5eEjhrm57yCF4zuyrt + AR8DG7ahK2YAarADHRyxTuxH1qY7E5/CTQKYk9tIYsV4O05CKj7B8rBMtjVNjb4b + d7JwPW43Z3J6jo/gLlVdGSPg8vQDNVLl6sdDM4Pm1eJEzgR2JlqXDCRDUGNNsXH2 + qt9Ru8ykX7PAfF2Q3/qg1jkCAwEAAQ== + -----END PUBLIC KEY----- + """; + + publicKeyString = publicKeyString.replaceAll("-----BEGIN PUBLIC KEY-----", "").replaceAll("-----END PUBLIC KEY-----", "").replaceAll("\\s", ""); + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString); + + KeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + + return publicKey; + } + + private byte[] decryptKey(byte[] encryptedKeyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + PublicKey publicKey = getPublicKey(); + + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, publicKey); + + byte[] decryptedBytes = decryptCipher.doFinal(encryptedKeyBytes); + + return decryptedBytes; + } + + public class InvalidLicenseException extends Exception { + + public InvalidLicenseException(String message) { + super(message); + } + + public InvalidLicenseException(String message, Throwable cause) { + super(message, cause); + } + + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java new file mode 100644 index 0000000000..4ff7d262b7 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java @@ -0,0 +1,40 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2018 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.util; +import com.google.common.base.Charsets; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import org.apache.commons.lang3.StringUtils; +/** + * + * @author jayaram + */ +public class Md5HashUtil { + /** + * Returns MD5 hash value for the lower case value of the string provided. + * @param inp + * @return + */ + public static String getMD5MessageDigest(String inp) { + if (StringUtils.isNotBlank(inp)) { + HashFunction hf = Hashing.md5(); // Using despite its deprecation as md5 is good enough for our uses. + HashCode hc = hf.newHasher() + .putString(inp.toLowerCase(), Charsets.UTF_8) + .hash(); + return hc.toString(); + } + return ""; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java new file mode 100644 index 0000000000..79098ce650 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -0,0 +1,41 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +/** + * Creates default ObjectMapper + */ +public class ObjectMapperUtil { + + private static final ObjectMapperUtil instance = new ObjectMapperUtil(); + + public static ObjectMapperUtil getInstance() { + return instance; + } + + private ObjectMapperUtil() { + + } + + public ObjectMapper getDefaultObjectMapper() { + ObjectMapper defaultMapper = new ObjectMapper(); + defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + defaultMapper.registerModule(new JavaTimeModule()); + return defaultMapper; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties new file mode 100644 index 0000000000..2d9daa4c7b --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties @@ -0,0 +1,5 @@ + +# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license +# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template +OptionsCategory_Name_CyberTriage=CyberTriage +OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED new file mode 100644 index 0000000000..2d9daa4c7b --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED @@ -0,0 +1,5 @@ + +# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license +# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template +OptionsCategory_Name_CyberTriage=CyberTriage +OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.form new file mode 100644 index 0000000000..1aee51eb33 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.form @@ -0,0 +1,39 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java new file mode 100644 index 0000000000..fd235b580e --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java @@ -0,0 +1,141 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions; + +import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.swing.JPanel; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; + +/** + * Options panel for CyberTriage. + */ +public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { + private static final int MAX_SUBPANEL_WIDTH = 500; + + private static final Logger logger = Logger.getLogger(CTOptionsPanel.class.getName()); + + private final List subPanels; + + /** + * Creates new form CTOptions + */ + public CTOptionsPanel() { + initComponents(); + Collection coll = Lookup.getDefault().lookupAll(CTOptionsSubPanel.class); + Stream panelStream = coll != null ? coll.stream() : Stream.empty(); + this.subPanels = panelStream + .sorted(Comparator.comparing(p -> p.getClass().getSimpleName().toUpperCase())) + .collect(Collectors.toList()); + addSubOptionsPanels(this.subPanels); + } + + private void addSubOptionsPanels(List subPanels) { + for (int i = 0; i < subPanels.size(); i++) { + CTOptionsSubPanel subPanel = subPanels.get(i); + + subPanel.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { + CTOptionsPanel.this.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + } + }); + + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = i; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(i == 0 ? 5 : 0, 5, 5, 5); + gridBagConstraints.weighty = 0; + gridBagConstraints.weightx = 0; + + contentPane.add(subPanel, gridBagConstraints); + } + + GridBagConstraints verticalConstraints = new GridBagConstraints(); + verticalConstraints.gridx = 0; + verticalConstraints.gridy = subPanels.size(); + verticalConstraints.weighty = 1; + verticalConstraints.weightx = 0; + + JPanel verticalSpacer = new JPanel(); + + verticalSpacer.setMinimumSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); + verticalSpacer.setPreferredSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); + verticalSpacer.setMaximumSize(new Dimension(MAX_SUBPANEL_WIDTH, Short.MAX_VALUE)); + contentPane.add(verticalSpacer, verticalConstraints); + + + GridBagConstraints horizontalConstraints = new GridBagConstraints(); + horizontalConstraints.gridx = 1; + horizontalConstraints.gridy = 0; + horizontalConstraints.weighty = 0; + horizontalConstraints.weightx = 1; + + JPanel horizontalSpacer = new JPanel(); + contentPane.add(horizontalSpacer, horizontalConstraints); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); + contentPane = new javax.swing.JPanel(); + + setLayout(new java.awt.BorderLayout()); + + contentPane.setLayout(new java.awt.GridBagLayout()); + scrollPane.setViewportView(contentPane); + + add(scrollPane, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + @Override + public void saveSettings() { + subPanels.forEach(panel -> panel.saveSettings()); + } + + public void loadSavedSettings() { + subPanels.forEach(panel -> panel.loadSettings()); + } + + public boolean valid() { + return subPanels.stream().allMatch(panel -> panel.valid()); + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel contentPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java new file mode 100644 index 0000000000..93dfe4960c --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java @@ -0,0 +1,128 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +/** + * Options panel controller for CyberTriage. + */ +@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_CyberTriage", + iconBase = "com/basistech/df/cybertriage/autopsy/images/logo.png", + position = 999999, + keywords = "#OptionsCategory_Keywords_CyberTriage", + keywordsCategory = "CyberTriage") +public final class CTOptionsPanelController extends OptionsPanelController { + + private CTOptionsPanel panel; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private boolean changed; + + /** + * Component should load its data here. + */ + @Override + public void update() { + getPanel().loadSavedSettings(); + changed = false; + } + + /** + * This method is called when both the Ok and Apply buttons are pressed. It + * applies to any of the panels that have been opened in the process of + * using the options pane. + */ + @Override + public void applyChanges() { + if (changed) { + getPanel().saveSettings(); + changed = false; + } + } + + /** + * This method is called when the Cancel button is pressed. It applies to + * any of the panels that have been opened in the process of using the + * options pane. + */ + @Override + public void cancel() { + } + + @Override + public boolean isValid() { + return getPanel().valid(); + } + + /** + * Used to determine whether any changes have been made to this controller's + * panel. + * + * @return Whether or not a change has been made. + */ + @Override + public boolean isChanged() { + return changed; + } + + @Override + public HelpCtx getHelpCtx() { + return null; + } + + @Override + public JComponent getComponent(Lookup masterLookup) { + return getPanel(); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + pcs.addPropertyChangeListener(l); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + pcs.removePropertyChangeListener(l); + } + + private CTOptionsPanel getPanel() { + if (panel == null) { + panel = new CTOptionsPanel(); + panel.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { + changed(); + } + } + }); + } + return panel; + } + + void changed() { + if (!changed) { + changed = true; + pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); + } + pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties new file mode 100644 index 0000000000..5df431e10c --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties @@ -0,0 +1,23 @@ + +# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license +# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template + + +CTLicenseDialog.licenseNumberLabel.text=License Number: +CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +CTLicenseDialog.cancelButton.text=Cancel +CTLicenseDialog.okButton.text=Ok +CTLicenseDialog.warningLabel.text= +CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text= +CTMalwareScannerOptionsPanel.countersResetLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title=License Info +CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text= +CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text= +CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text= +CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scans +CTMalwareScannerOptionsPanel.licenseInfoAddButton.text=Add License +CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= +CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED new file mode 100644 index 0000000000..6eaa730072 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED @@ -0,0 +1,55 @@ + +# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license +# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template + + +CTLicenseDialog.licenseNumberLabel.text=License Number: +CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +CTLicenseDialog.cancelButton.text=Cancel +CTLicenseDialog.okButton.text=Ok +CTLicenseDialog.warningLabel.text= +CTLicenseDialog_verifyInput_licenseNumberError=Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' +CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text= +CTMalwareScannerOptionsPanel.countersResetLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title=License Info +CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text= +CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text= +CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text= +CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scans +CTMalwareScannerOptionsPanel.licenseInfoAddButton.text=Add License +CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= +CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= +CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= +CTMalwareScannerOptionsPanel_licenseAddDialog_desc=License Number: +CTMalwareScannerOptionsPanel_licenseAddDialog_title=Add a License... +CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc=The license number has already been entered +CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_title=License Number Already Entered +CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_desc=Please verify that license number is of format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' +CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_title=Invalid License Number +CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error +CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later. +CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error +# {0} - expiresDate +CTMalwareScannerOptionsPanel_licenseInfo_expires=Expires: {0} +# {0} - idNumber +CTMalwareScannerOptionsPanel_licenseInfo_id=ID: {0} +# {0} - userName +# {1} - email +CTMalwareScannerOptionsPanel_licenseInfo_userInfo=User: {0}
Email: {1} +# {0} - countersResetDate +CTMalwareScannerOptionsPanel_malwareScans_countersReset=Counters reset: {0} +# {0} - fileUploadsRemaining +CTMalwareScannerOptionsPanel_malwareScans_fileUploadsRemaining=File uploads remaining: {0} +# {0} - hashLookupsRemaining +CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining=Hash lookups remaining: {0} +# {0} - maxDailyFileLookups +CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}/day +# {0} - maxDailyLookups +CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}/day +CTMalwareScannerOptionsPanel_MalwareScansFetcher_apiErr_title=Server Error +CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_desc=A general error occurred while fetching malware scans information. Please try again later. +CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_title=General Error +CTOPtionsPanel_loadLicenseInfo_loading=Loading... +CTOPtionsPanel_loadMalwareScansInfo_loading=Loading... diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.form new file mode 100644 index 0000000000..e7cd2743a0 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.form @@ -0,0 +1,138 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java new file mode 100644 index 0000000000..8af5795517 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java @@ -0,0 +1,192 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; + +import java.util.regex.Pattern; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle.Messages; + +/** + * License dialog + */ +public class CTLicenseDialog extends javax.swing.JDialog { + + private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[0-9a-zA-Z]{8}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{12}\\s*$"); + + private String licenseString = null; + + /** + * Creates new form CTLicenseDialog + */ + public CTLicenseDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + this.licenseNumberTextField.getDocument().putProperty("filterNewlines", Boolean.TRUE); + this.licenseNumberTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + verifyInput(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + verifyInput(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + verifyInput(); + } + }); + } + + String getValue() { + return licenseString; + } + + @Messages({ + "CTLicenseDialog_verifyInput_licenseNumberError=Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'" + }) + private void verifyInput() { + String licenseInput = StringUtils.defaultString(this.licenseNumberTextField.getText()); + if (LICENSE_PATTERN.matcher(licenseInput).matches()) { + this.warningLabel.setText(""); + this.okButton.setEnabled(true); + } else { + this.warningLabel.setText(Bundle.CTLicenseDialog_verifyInput_licenseNumberError()); + this.okButton.setEnabled(false); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.JLabel licenseNumberLabel = new javax.swing.JLabel(); + warningLabel = new javax.swing.JLabel(); + javax.swing.JPanel buttonPadding = new javax.swing.JPanel(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + licenseNumberTextField = new javax.swing.JTextField(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.title")); // NOI18N + setAlwaysOnTop(true); + setResizable(false); + getContentPane().setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(licenseNumberLabel, org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.licenseNumberLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(licenseNumberLabel, gridBagConstraints); + + warningLabel.setForeground(java.awt.Color.RED); + org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.warningLabel.text")); // NOI18N + warningLabel.setMaximumSize(new java.awt.Dimension(419, 36)); + warningLabel.setMinimumSize(new java.awt.Dimension(419, 36)); + warningLabel.setPreferredSize(new java.awt.Dimension(419, 36)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + getContentPane().add(warningLabel, gridBagConstraints); + + javax.swing.GroupLayout buttonPaddingLayout = new javax.swing.GroupLayout(buttonPadding); + buttonPadding.setLayout(buttonPaddingLayout); + buttonPaddingLayout.setHorizontalGroup( + buttonPaddingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + buttonPaddingLayout.setVerticalGroup( + buttonPaddingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.weightx = 1.0; + getContentPane().add(buttonPadding, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + getContentPane().add(okButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + getContentPane().add(cancelButton, gridBagConstraints); + + licenseNumberTextField.setText(org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.licenseNumberTextField.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + getContentPane().add(licenseNumberTextField, gridBagConstraints); + + pack(); + }// //GEN-END:initComponents + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + this.licenseString = this.licenseNumberTextField.getText(); + this.dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + this.licenseString = null; + this.dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancelButton; + private javax.swing.JTextField licenseNumberTextField; + private javax.swing.JButton okButton; + private javax.swing.JLabel warningLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java new file mode 100644 index 0000000000..8f9d61a79e --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java @@ -0,0 +1,90 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; + +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; + +/** + * Handles persisting CT Settings. + */ +public class CTLicensePersistence { + + private static final String CT_SETTINGS_DIR = "CyberTriage"; + private static final String CT_LICENSE_FILENAME = "CyberTriageLicense.json"; + + private static final Logger logger = Logger.getLogger(CTLicensePersistence.class.getName()); + + private static final CTLicensePersistence instance = new CTLicensePersistence(); + + private final ObjectMapper objectMapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper(); + + public static CTLicensePersistence getInstance() { + return instance; + } + + public synchronized boolean saveLicenseResponse(LicenseResponse licenseResponse) { + if (licenseResponse != null) { + File licenseFile = getCTLicenseFile(); + try { + objectMapper.writeValue(licenseFile, licenseResponse); + return true; + } catch (IOException ex) { + logger.log(Level.WARNING, "There was an error writing CyberTriage license to file: " + licenseFile.getAbsolutePath(), ex); + } + } + + return false; + } + + public synchronized Optional loadLicenseResponse() { + Optional toRet = Optional.empty(); + File licenseFile = getCTLicenseFile(); + if (licenseFile.isFile()) { + try { + toRet = Optional.ofNullable(objectMapper.readValue(licenseFile, LicenseResponse.class)); + } catch (IOException ex) { + logger.log(Level.WARNING, "There was an error reading CyberTriage license to file: " + licenseFile.getAbsolutePath(), ex); + } + } + + return toRet; + } + + public synchronized Optional loadLicenseInfo() { + return loadLicenseResponse().flatMap((license) -> { + try { + return Optional.ofNullable(LicenseDecryptorUtil.getInstance().createLicenseInfo(license)); + } catch (JsonProcessingException | LicenseDecryptorUtil.InvalidLicenseException ex) { + logger.log(Level.WARNING, "There was an error decrypting license data from license file", ex); + return Optional.empty(); + } + }); + } + + private File getCTLicenseFile() { + return Paths.get(PlatformUtil.getModuleConfigDirectory(), CT_SETTINGS_DIR, CT_LICENSE_FILENAME).toFile(); + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form new file mode 100644 index 0000000000..77361419b6 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form @@ -0,0 +1,199 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java new file mode 100644 index 0000000000..069d9fb653 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -0,0 +1,551 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; + +import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; +import com.basistech.df.cybertriage.autopsy.ctapi.CTCloudException; +import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.text.SimpleDateFormat; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.openide.windows.WindowManager; + +/** + * Options panel for CyberTriage options for importing a CyberTriage incident + */ +@ServiceProvider(service = CTOptionsSubPanel.class) +public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { + + private static final Logger logger = Logger.getLogger(CTMalwareScannerOptionsPanel.class.getName()); + + private static final SimpleDateFormat LICENSE_EXPIRES_FORMAT = new SimpleDateFormat("MMMM d, YYYY"); + private static final SimpleDateFormat MALWARE_SCANS_RESET_FORMAT = new SimpleDateFormat("MMM d, YYYY' at 'h:mma"); + + private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); + private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance(); + + private volatile LicenseInfo licenseInfo = null; + private volatile String licenseInfoMessage = null; + private volatile LicenseFetcher licenseFetcher = null; + + private volatile AuthTokenResponse authTokenResponse = null; + private volatile String authTokenMessage = null; + private volatile AuthTokenFetcher authTokenFetcher = null; + + /** + * Creates new form CTIncidentImportOptionsPanel + */ + public CTMalwareScannerOptionsPanel() { + initComponents(); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentHidden(ComponentEvent e) { + synchronized (CTMalwareScannerOptionsPanel.this) { + if (CTMalwareScannerOptionsPanel.this.isLicenseAddRunning()) { + CTMalwareScannerOptionsPanel.this.licenseFetcher.cancel(true); + CTMalwareScannerOptionsPanel.this.licenseFetcher = null; + } + + if (CTMalwareScannerOptionsPanel.this.isMalwareScansRunning()) { + CTMalwareScannerOptionsPanel.this.authTokenFetcher.cancel(true); + CTMalwareScannerOptionsPanel.this.authTokenFetcher = null; + } + } + } + + @Override + public void componentShown(ComponentEvent e) { + synchronized (CTMalwareScannerOptionsPanel.this) { + if (CTMalwareScannerOptionsPanel.this.licenseInfo != null) { + loadMalwareScansInfo(CTMalwareScannerOptionsPanel.this.licenseInfo); + } + } + } + } + ); + } + + @Override + public synchronized void saveSettings() { + ctPersistence.saveLicenseResponse(getLicenseInfo()); + } + + @Override + public boolean valid() { + return true; + } + + @Override + public synchronized void loadSettings() { + Optional licenseInfoOpt = ctPersistence.loadLicenseInfo(); + LicenseInfo licenseInfo = licenseInfoOpt.orElse(null); + setLicenseDisplay(licenseInfo, null); + setMalwareScansDisplay(null, null); + if (licenseInfo != null) { + loadMalwareScansInfo(licenseInfo); + } + } + + private synchronized LicenseResponse getLicenseInfo() { + return this.licenseInfo == null ? null : this.licenseInfo.getLicenseResponse(); + } + + private synchronized void setLicenseDisplay(LicenseInfo licenseInfo, String licenseMessage) { + this.licenseInfo = licenseInfo; + this.licenseInfoMessage = licenseMessage; + renderLicenseState(); + } + + private synchronized void setMalwareScansDisplay(AuthTokenResponse authTokenResponse, String authTokenMessage) { + this.authTokenResponse = authTokenResponse; + this.authTokenMessage = authTokenMessage; + renderLicenseState(); + } + + /** + * @return True if there is an operation to fetch the license. + */ + private synchronized boolean isLicenseAddRunning() { + return this.licenseFetcher != null && !this.licenseFetcher.isCancelled() && !this.licenseFetcher.isDone(); + } + + /** + * @return True if there is an operation to fetch malware scans information. + */ + private synchronized boolean isMalwareScansRunning() { + return this.authTokenFetcher != null && !this.authTokenFetcher.isCancelled() && !this.authTokenFetcher.isDone(); + } + + @Messages({ + "CTOPtionsPanel_loadLicenseInfo_loading=Loading..." + }) + private synchronized void loadLicenseInfo(String licenseNumber) { + if (isLicenseAddRunning()) { + this.licenseFetcher.cancel(true); + } + setLicenseDisplay(null, Bundle.CTOPtionsPanel_loadLicenseInfo_loading()); + this.licenseFetcher = new LicenseFetcher(licenseNumber); + this.licenseFetcher.execute(); + } + + @Messages({ + "CTOPtionsPanel_loadMalwareScansInfo_loading=Loading..." + }) + private synchronized void loadMalwareScansInfo(LicenseInfo licenseInfo) { + if (isMalwareScansRunning()) { + this.authTokenFetcher.cancel(true); + } + + setMalwareScansDisplay(null, Bundle.CTOPtionsPanel_loadMalwareScansInfo_loading()); + + if (licenseInfo == null || licenseInfo.getDecryptedLicense() == null) { + return; + } + + this.authTokenFetcher = new AuthTokenFetcher(licenseInfo.getDecryptedLicense().getBoostLicenseId()); + this.authTokenFetcher.execute(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.JPanel licenseInfoPanel = new javax.swing.JPanel(); + licenseInfoMessageLabel = new javax.swing.JLabel(); + licenseInfoUserLabel = new javax.swing.JLabel(); + licenseInfoExpiresLabel = new javax.swing.JLabel(); + licenseInfoIdLabel = new javax.swing.JLabel(); + licenseInfoAddButton = new javax.swing.JButton(); + malwareScansPanel = new javax.swing.JPanel(); + malwareScansMessageLabel = new javax.swing.JLabel(); + maxHashLookupsLabel = new javax.swing.JLabel(); + maxFileUploadsLabel = new javax.swing.JLabel(); + countersResetLabel = new javax.swing.JLabel(); + hashLookupsRemainingLabel = new javax.swing.JLabel(); + fileUploadsRemainingLabel = new javax.swing.JLabel(); + + setLayout(new java.awt.GridBagLayout()); + + licenseInfoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title"))); // NOI18N + licenseInfoPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(licenseInfoMessageLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + licenseInfoPanel.add(licenseInfoMessageLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(licenseInfoUserLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + licenseInfoPanel.add(licenseInfoUserLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(licenseInfoExpiresLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + licenseInfoPanel.add(licenseInfoExpiresLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(licenseInfoIdLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + licenseInfoPanel.add(licenseInfoIdLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(licenseInfoAddButton, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoAddButton.text")); // NOI18N + licenseInfoAddButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + licenseInfoAddButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + licenseInfoPanel.add(licenseInfoAddButton, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + add(licenseInfoPanel, gridBagConstraints); + + malwareScansPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.malwareScansPanel.border.title"))); // NOI18N + malwareScansPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(malwareScansMessageLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + malwareScansPanel.add(malwareScansMessageLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(maxHashLookupsLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + malwareScansPanel.add(maxHashLookupsLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(maxFileUploadsLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + malwareScansPanel.add(maxFileUploadsLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(countersResetLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.countersResetLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + malwareScansPanel.add(countersResetLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(hashLookupsRemainingLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + malwareScansPanel.add(hashLookupsRemainingLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(fileUploadsRemainingLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + malwareScansPanel.add(fileUploadsRemainingLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + add(malwareScansPanel, gridBagConstraints); + }// //GEN-END:initComponents + + @Messages({ + "CTMalwareScannerOptionsPanel_licenseAddDialog_title=Add a License...", + "CTMalwareScannerOptionsPanel_licenseAddDialog_desc=License Number:", + "CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_title=License Number Already Entered", + "CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc=The license number has already been entered", + "CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_title=Invalid License Number", + "CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_desc=Please verify that license number is of format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'"}) + private void licenseInfoAddButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_licenseInfoAddButtonActionPerformed + CTLicenseDialog licenseDialog = new CTLicenseDialog(WindowManager.getDefault().getMainWindow(), true); + licenseDialog.setLocationRelativeTo(this); + licenseDialog.setVisible(true); + String licenseNumber = licenseDialog.getValue(); + if (licenseNumber != null) { + synchronized (this) { + if (this.licenseInfo == null || !licenseNumber.trim().equalsIgnoreCase(this.licenseInfo.getDecryptedLicense().getBoostLicenseId())) { + loadLicenseInfo(licenseNumber); + return; + } + } + + JOptionPane.showMessageDialog( + this, + Bundle.CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc(), + Bundle.CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_title(), + JOptionPane.INFORMATION_MESSAGE); + + } + }//GEN-LAST:event_licenseInfoAddButtonActionPerformed + + @NbBundle.Messages({ + "# {0} - userName", + "# {1} - email", + "CTMalwareScannerOptionsPanel_licenseInfo_userInfo=User: {0}
Email: {1}", + "# {0} - expiresDate", + "CTMalwareScannerOptionsPanel_licenseInfo_expires=Expires: {0}", + "# {0} - idNumber", + "CTMalwareScannerOptionsPanel_licenseInfo_id=ID: {0}", + "# {0} - maxDailyLookups", + "CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}/day", + "# {0} - maxDailyFileLookups", + "CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}/day", + "# {0} - countersResetDate", + "CTMalwareScannerOptionsPanel_malwareScans_countersReset=Counters reset: {0}", + "# {0} - hashLookupsRemaining", + "CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining=Hash lookups remaining: {0}", + "# {0} - fileUploadsRemaining", + "CTMalwareScannerOptionsPanel_malwareScans_fileUploadsRemaining=File uploads remaining: {0}"}) + private synchronized void renderLicenseState() { + this.licenseInfoAddButton.setEnabled(!isLicenseAddRunning()); + + this.licenseInfoMessageLabel.setVisible(StringUtils.isNotBlank(this.licenseInfoMessage)); + this.licenseInfoMessageLabel.setText(this.licenseInfoMessage); + + if (licenseInfo == null) { + this.licenseInfoExpiresLabel.setVisible(false); + this.licenseInfoIdLabel.setVisible(false); + this.licenseInfoUserLabel.setVisible(false); + } else { + this.licenseInfoExpiresLabel.setVisible(true); + this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires(LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); + this.licenseInfoIdLabel.setVisible(true); + this.licenseInfoIdLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_id(this.licenseInfo.getDecryptedLicense().getBoostLicenseId())); + this.licenseInfoUserLabel.setVisible(true); + this.licenseInfoUserLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_userInfo(this.licenseInfo.getUser(), this.licenseInfo.getEmail())); + } + + this.malwareScansPanel.setVisible(StringUtils.isNotBlank(this.authTokenMessage) || authTokenResponse != null); + + this.malwareScansMessageLabel.setVisible(StringUtils.isNotBlank(this.authTokenMessage)); + this.malwareScansMessageLabel.setText(this.authTokenMessage); + + if (authTokenResponse == null) { + this.maxHashLookupsLabel.setVisible(false); + this.maxFileUploadsLabel.setVisible(false); + this.countersResetLabel.setVisible(false); + this.hashLookupsRemainingLabel.setVisible(false); + this.fileUploadsRemainingLabel.setVisible(false); + } else { + this.maxHashLookupsLabel.setVisible(true); + this.maxHashLookupsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups(this.authTokenResponse.getHashLookupLimit())); + this.maxFileUploadsLabel.setVisible(true); + this.maxFileUploadsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups(this.authTokenResponse.getFileUploadLimit())); + this.countersResetLabel.setVisible(true); + this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getExpiration()))); + this.hashLookupsRemainingLabel.setVisible(true); + this.hashLookupsRemainingLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining(remaining(this.authTokenResponse.getHashLookupLimit(), this.authTokenResponse.getHashLookupCount()))); + this.fileUploadsRemainingLabel.setVisible(true); + this.fileUploadsRemainingLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_fileUploadsRemaining(remaining(this.authTokenResponse.getFileUploadLimit(), this.authTokenResponse.getFileUploadCount()))); + } + } + + private long remaining(Long total, Long used) { + total = total == null ? 0 : total; + used = used == null ? 0 : used; + return total - used; + } + + @NbBundle.Messages({ + "CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error", + "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error", + "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later.",}) + private class LicenseFetcher extends SwingWorker { + + private final String licenseText; + + public LicenseFetcher(String licenseText) { + this.licenseText = licenseText; + } + + @Override + protected LicenseInfo doInBackground() throws Exception { + LicenseResponse licenseResponse = ctApiDAO.getLicenseInfo(licenseText); + ctPersistence.saveLicenseResponse(licenseResponse); + return LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); + } + + @Override + protected void done() { + LicenseInfo licenseInfo = null; + try { + licenseInfo = get(); + } catch (InterruptedException ex) { + // ignore cancellation + } catch (ExecutionException ex) { + if (ex.getCause() != null && ex.getCause() instanceof CTCloudException cloudEx) { + logger.log(Level.WARNING, "An API error occurred while fetching license information", cloudEx); + JOptionPane.showMessageDialog( + CTMalwareScannerOptionsPanel.this, + cloudEx.getErrorCode().getDescription(), + Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title(), + JOptionPane.ERROR_MESSAGE); + } else { + logger.log(Level.WARNING, "An error occurred while fetching data", ex); + JOptionPane.showMessageDialog( + CTMalwareScannerOptionsPanel.this, + Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc(), + Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title(), + JOptionPane.ERROR_MESSAGE); + } + + } finally { + + synchronized (CTMalwareScannerOptionsPanel.this) { + CTMalwareScannerOptionsPanel.this.licenseFetcher = null; + if (!this.isCancelled()) { + setLicenseDisplay(licenseInfo, null); + loadMalwareScansInfo(licenseInfo); + } + } + } + } + } + + @NbBundle.Messages({ + "CTMalwareScannerOptionsPanel_MalwareScansFetcher_apiErr_title=Server Error", + "CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_title=General Error", + "CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_desc=A general error occurred while fetching malware scans information. Please try again later.",}) + private class AuthTokenFetcher extends SwingWorker { + + private final String boostLicenseId; + + public AuthTokenFetcher(String boostLicenseId) { + this.boostLicenseId = boostLicenseId; + } + + @Override + protected AuthTokenResponse doInBackground() throws Exception { + return ctApiDAO.getAuthToken(boostLicenseId); + } + + @Override + protected void done() { + AuthTokenResponse authTokenResponse = null; + try { + authTokenResponse = get(); + } catch (InterruptedException ex) { + // ignore cancellation + } catch (ExecutionException ex) { + if (ex.getCause() != null && ex.getCause() instanceof CTCloudException cloudEx) { + logger.log(Level.WARNING, "An API error occurred while fetching malware scans information for license", cloudEx); + JOptionPane.showMessageDialog( + CTMalwareScannerOptionsPanel.this, + cloudEx.getErrorDetails(), + Bundle.CTMalwareScannerOptionsPanel_MalwareScansFetcher_apiErr_title(), + JOptionPane.ERROR_MESSAGE); + } else { + logger.log(Level.WARNING, "An error occurred while fetching data", ex); + JOptionPane.showMessageDialog( + CTMalwareScannerOptionsPanel.this, + Bundle.CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_desc(), + Bundle.CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_title(), + JOptionPane.ERROR_MESSAGE); + } + } finally { + synchronized (CTMalwareScannerOptionsPanel.this) { + CTMalwareScannerOptionsPanel.this.authTokenFetcher = null; + if (!this.isCancelled()) { + setMalwareScansDisplay(authTokenResponse, null); + } + } + } + } + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel countersResetLabel; + private javax.swing.JLabel fileUploadsRemainingLabel; + private javax.swing.JLabel hashLookupsRemainingLabel; + private javax.swing.JButton licenseInfoAddButton; + private javax.swing.JLabel licenseInfoExpiresLabel; + private javax.swing.JLabel licenseInfoIdLabel; + private javax.swing.JLabel licenseInfoMessageLabel; + private javax.swing.JLabel licenseInfoUserLabel; + private javax.swing.JLabel malwareScansMessageLabel; + private javax.swing.JPanel malwareScansPanel; + private javax.swing.JLabel maxFileUploadsLabel; + private javax.swing.JLabel maxHashLookupsLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java new file mode 100644 index 0000000000..67f727bb13 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java @@ -0,0 +1,26 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctoptions.subpanel; + +import javax.swing.JPanel; + +/** + * A panel to be put in the CyberTriage options. + */ + +public abstract class CTOptionsSubPanel extends JPanel { + public abstract void loadSettings(); + public abstract void saveSettings(); + public abstract boolean valid(); +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/images/logo.png b/Core/src/com/basistech/df/cybertriage/autopsy/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7f5ab5ba4c2cd031b422d0ed333d665261a39af8 GIT binary patch literal 10482 zcmeHtcU03$w|3}Nnu3ZT0i+2bA+!LYNDsaD7Lov=1w*Jxk)kvyQlu$LN9hPkQBXlZ z0hKBp>Ai@Eh{6r%Imh$e_j~X6uJzsj4r`^%%(I`p=b61{@|#7pfu6<*T6S6h0B{1K zscJ<01RlMpDT(jifq|a^0Hzy(CKd!Eq#w`=k8{GhV}OJJFANalk97h7{71?%-0(9n zmWacdQ&-5RPjFDZje7e0d`L67kfpDmzRqeqCYD9*nv$uR6Px>B$idU@=C3pgd?y`6 zO9}~FCtZunw{mTE_2u4vpDR-L8az zbA8_6>femB52pV-HtePzD4%m}Sv=8im+H&ed*g2!j6i$dG(qF-dsc}0!??T3dkSmz z6VAEPyt`XHAl#3(n(g(wp+h;+&$f8BOA|6G85ex&-ztVPCr-94eM?W>%Shc7-oLS} zcx$>ZXu@my3Z!rU>*%)B0ql_8MK4NmeYJhRXR;^Z@>x zu1(pFp_Aj@U2u!mm6(Y0U*BZIw<ik+b=wnBY z>J`yAjt#4t00lB8o^{TQxE=JA)}Th#YU-UggDmaJ!R%zL3*jsIEw2{&YY5NxRq-)> zPxtid7OYN>2eJ3?FuxbjdYE`my_~d_G8ixDP^`wK5+~Nip@ zhgqEiJ8RfgoTn@OBSOF_Ga${<+1i(+fYrp@n#=J(-p(H*~GpT~ZKrS*N^ z-pp(`cR#D1*DE9-gPm_y%ztNp5u=xjSAS(wiG~Z40Y-$J&8xU!dHuxC$oZ>;F6c#> zzKbX{L}X>UZeuJ6vu-`1Lw#sIF(&M-Ua~I$bxvteM6VAu{or>qb`=-J>0LLuPP;rO zq$uiIz8TB^W9bQgxy!%t!yx;+4JmB=u>S@7hO(-+nd1-Y-x^xEi8suDbCvpbH_yI` zxkbKWf`*T)mI1M5GA+?m@}ANk>__c2a)vsp;&x6q%k&_1GiRGH9J4t7blKAX2=nq*B&sNHg!os+D;BO0ci z-M)-@q;N(Bo})4Oo_pB*A!_^8$(M$DcHhQ=X#zawn`S!1N0-z;gu0~CE8lnF3eaa^tGO<^W2@s@iw5@)lwFbeyPkpQ~s^e(`|ea3`%RWkL8^KsQH4L&tFGU9^oeKt;Yr|nNr@jqw;4_jV z;2PFsnJ3`pZyxyCP!%+!M5#XSXZNGdw)!x`9!@BhP*|%I18a@z7TvFJSM#MVULcf; zr_@tM^D^A?axz;7#zD3rSP}dQZ}MA@?qc#yCZ^))(yZcYY%VO)rLirNt1yyvlk;ed ztHUC`10>^WOB5S})~fl+jPJ`TnNMY7x)t`usZOm_>oEM-yl}a~gQq0!VsucqS(*jq zRq-p@$S+Rj*BL~1l*)#vg8E-*fsATXQd!f}B*#|6=AZN}S3wHg(yF-Ux+`{0N=)*Z zA*0<|TL<`-mu2oLm9JapyKJ3Cj0(XuHkQ}-fPvOp1h=(_@Gy3eKI2;I zYaww;bDr}ih~8F>_P8?HH7y8AYnJg9iYB(8mm=;XMMxN{DU_BqmScR?awGa8nWJ59PL~Xi`AuH76T7caPC*T@Rnn|Kqe6GjL&QtlDI?2?Z2R0* z+lGfB>oE(^B6k-{{K`d^#xS5xq~9h(V@gV(QAF&!@X#tfEi$dJew=8Vmb7!A=BCzq zerPCAvzPNm&%Lqu+;p8z@62hJEPoF=4xS3`IqIpcI$eTgQD}msuC}oP$2w z@Vz^t#fsgG5dU6ONl-{nB#oo2hSmI7%?^wZ|INeGTM1E$0sE@GNcy{ zt|qy5v>EoB}6)nA9>GTEQSR$XI1F4q(clKu0vuz{(9pUlGOreU@DAIR3GCZdOx^FPdz-Y_k z8d%imm;f>Cu(=a@^Co^veyIshtD*Ik))vgG4#A6u(U-6`yZjD%t$ab^_)}1`{5lha zEQzVD=qd7t@AB~UxZJ3`mM2R=&X$0-5_gx}bHxtHi-Zq=Xn~TH9a)NtauHEjkt?^o zNZLrQzAQG88c^wEahz>ejg`43Spu65BWsx^Tss-k)JKO|qk3)A%oI7tWYY}syHgPT za9`K;)?1P0TXN0)Z)`W>bZC5JyqYvEA~WyzSAXR?pBnC-ZfMHBsjG5f?M-a5Q0gSx z$hOre>@D*`hiqIE?3)soUUfb1bfwo6>_kLleXdAdXG>`t*Em2k4>YXN9S`noe{1+i zRb^LzErs>u2Uxb3bK#5cCr&!>kp?wpu_hM9SPZIw=X}NAC}qxk0l%|z;{PGL>NllZ zy|pE{+`1wfnGwk9U4Grlk^WSk05f&$^ZF28BpPao9sJixyOBK#iGQ~!-6WbvK$ zqovfkL6lOgi1o(^0Z_xh4~csO`qw_5DzC2AJ?*$LwX&Y8#pI<+S7uq3NrClqg>*a* z=Zs)(i_K^5Ed}@>4}_AEDhKKKmOpD-8E=~_7|4m#cEze0JZi39!|lh9)XZ8WiPoy? zTx21QTVc`|5W1kt)T5MdRodAdc%EH|PCQ*i%ls(xL5kn>f_UUf)0q!Z#VH z7uBR%t0~-F-Di47pOGoA0{91!wme#$O*Zok5(f!%=j|h%gH5K|nX7dx0r>zkZsP{6 zv&@1l3UUYbI~pkv>T<_~`{bk0#Y<wOQCU!M$Xt|WMJim|5p}^1ql{b zUc|Xwi5RV{;@3Sc0P_$*#AkXkcfZHBdq!M7dFrir@$|snsIv?Ei_UHReGYcKXJ4=S z&$zw>*G8_W)`o&iS`7zw()rLdKU(i|HL8X$(GP3)s#n;NxW$=PcR03BzIW*DcHDc; zNRaya^<<+LpV3pjuL2`EyOqm1Tx-`af$6`qVYxPzaEIb=U-k(Cf<&OeDJdBB&R`|2 zc1@HDtLS%nQmXUqW-{!eF9KX48GM_Ox<<`POVK+C`Y(h$0*YLK7fCY)PJKOhGF_Cm z5d^((Qi~>;p^&~J`)dPtt+`4r0k^1O{an2ktOVZ^FnRfO7uI9gNP*KZYQK6#XSc(4 zJDBfeYl+T{`vK~=&)H5z9rW?%&*h*xa_^ z{={FU*i)gh7w%4vXyYsf-^Xp>A-a#s?(k(|=W{ShHBqc1?IKdE9g!T}DD9*(owzh! z=IEp|55`$69{X@nAK1AD?o|r85wiWw8a*@}p1LprF>s0@`rG&TS_$adJAwynd4^{` zJ>at?@YAuv(9MMg(Z=rcvy{0gh24X9@%swBk#&BjW7Z>X&jffdfHEhw6z$$$Y2e+| zP-Z)k#JBqKtJTQoOwYslTEpZC5vXv)lcnvqQStMXiHWI%9xNv@MOab@_PSDouNIbY-< z_b#QG(hCuZ$#HO=ZtMuqG}pKqcodd-8qogo>1$`lQ_(V!(4nE!1C4c-1V`N z=%I;doi@67Upvba4h@aHa|NX|3mUQeuKA~<3%Rcc)xR5Syg85)gsR`0JL?S7@*?6&R-)sFme8h7D#a`riA@m_6;IwZ zwzubOV~4IY^$i6x_JA0w`=yw3PVVVtFa%8wHZoZ4A}6=2N9bQfB}@8od?zoB@ogN& zx`|tM6%Df)rPRN5FKx8x-~xRPWxV(F5|@fdr2Fc4%t(>yE$;zeo&25lp_LH2*UxW< zU$abN2#MV+5^7sz=MY>~0zo?Oio+k2k>Bh<7|ggOHxpgM%>3tt*t|Ljmw4d2|Bp5z(U?q=YR=|Mm8k8u>eDper+ z$@UJ@hH+-I+5A~21D>4j&cnfom_8L~ceR1wU2>3Dpgu_?Y+kk7sjrZyO!;wgfYFz@ zn6XttdTV!l{`CDW?WnNB9eeT$U1-D!s2a!Ud(m$KA>RZn`!zzcl4_ONTI@8;2dPd^ zeX?0|y_t|un&)C|EoVS@HFC0R3q>_a?m@S@-0ZuzV*KQeum6DBzU@Qb3$su&%hO4D zWc=SRbgR_UZ71~J?WQ!}@;cK6S5Mk19m^o)j+GiK>P#CtZ#sC$y{M;5x6guAPZW@n z8VI1)7w=%b+=WnX7WKoWUNK22e! z$wI%coa0+UO|$o|jC|~Q4#L^!Z#dO-gN8v}H7f=-dED)=6>id804Nbv z6ms~k!lXDVo@=N)1i<~0deQ&-Qu>$9xbo;%8!pUM_?M|>K)PUF1v{&?rU6f@wCx}c zMw|ZT7Pn`4XI~WTK@TZJUz#zlI{I+|03?xE6%_-7ipn2X62yy$?4Trh&6i3XZT7|m zMi*mznG8D&?ubOiz_eIn9qvD3weo?~e^znnP0W8n{24?+rO5V>LA0kq_6cp+6jjv2 z*yP;Ul&{VC?|5E?&jdF$?moFfMZE(x?vB5#G$6vunh-ra7IVg54RGo6{nIa5#CKE! zxyjyc`7mi!X+?CfMAz z4W6+fSWIOgd6P4F8BWHwNu`aZr_!hT$G~!n+2`miZ?pDTXAj_0B{w?l&%AbfgDvml zQ09o$1kx}=AAC?c%a;4dH9uYReQ?)H#l^45v$r~HNz~&5OztV;zfXm1Hd@y}WIpmo zIw~#1dW6IQuG!u*tXbPYje2GS4xFd7-1dZh9OjchTWQB~kjRg=wE1W!?}onNbeKZi zrteJpE)l6r;;)#8drk7=v69!oT3LNU9~Dr=7Bmi^y3yj2R6PWr7$2AzZFbK5@dIF4 zg}+0wcZV7PV9dr6?}#jPb!1UE4^bo<=ZF#Y_wXX#AprnzMSm|O$_+yRI%1r$p7J~! zjm!pHm!D=NeN697K!Xv)h6HgwL;x5HfxtizCEn$#>N<|w9qj}r26gQDcN-0auEC!+M>0BNJ-m)Bj?lj&(WpOh zUOssDV+X zgr_$F>50M|Q4z^Su|ys*aj3W?3-i7i6o{Q)&uE`0eg8mAM+k9j4W|FL}`&n)j?!F?kCPh zRt1ki5^#7E9L`;y=O|j>5#@1F0pY*OMOFugIyO88W6(zx_p9cVkHelnc_+8AIG}e-+yw z{n&qz$WjmpMjC~Z06{U1G9U?Y;+rE9;t0YZWnhwMF$qU$r=J<|C%ZS!iQtFCV=g%p zbAY(c#Ns+$XQ1G({1f_nG=46aqvV1>#X%5hkQl^7OhQ%+CJT|_`MKIaIQVFX{bRQ9 zqdiDhSN3;3;75CuEaE5;jeWel+_4z^-*x&UZ~ia1-|TKH?JM5>m3eGElSj8>` zeLv5C>;7K=e=_J`Q5a8e+`kL`cgRm!emjVWG5_o%9&p6-68z_Z`729~3h96G?^pKy zFM1$S|KsF8()S;^{*miHQs6%V|C3$+$n_s7@E?Ky$*%uza?$?v$c^zN{_gW5K2Xb6 zh8Gea#wi`OHBTf&SuGqNy>CK#G*aebX5)sVzt<=IYtyDKDE*WT%-Z~v2 zM6r@D&ZkL3E~59G?H!O}wm@9%fer|KO_|={5-BH@vKl2OrZ?te8AoC(xw2^e*uLlC z6FJIh3Q?V$L*Lziy>*4gx7)Ut;-Cdu@_kdh@@(UG&7O^>2tP!@ogExn#(81E*a2VVWUzg?_WL{<}>$bk)jW;46aDqE;`sq9`4Z66~Vd^@{iU45Q zF80mHd{?6rmZ${lp9x}#sHovQF_GV-5a|n`FiVHlJ2~TJ_MM zGNIGvc}}wYJ+8tH3Aatb*@`?3SRFdEU@LONI;b;?s$$h__-1e+8QQ`!hqe7`R{qZX zOr~*}z~Mv08w;gY8XHSWI`I<|l*QXg(j`Xnr&&V>t+KJGRC`pl_OmMzsnrSFn7vAL zaJmyF&+;BXuiihzsrJBKE0rDJIMwNNpdgX4uPB<>d@iFiE1JIt02?d);IEa{Om**` zk-j_a<|(Cy=V6cUrh)*%8gClNBJm@<2zrFrwti?KKv7|7w!@osxk7yRL7WSKUfkL& zA?Y?i>xQPj{W)(yW;lK6w*=8=6fM>49*wg|%QJh?_VZDUva;W&iavd)gKwb^{rLsb zpPV9pzv$e%CuP?>U+=9|7kAsVgzW(dhuuWBaarIPU~`GpcA37{JhZ1$%#Dy1^{wa1 zaKynPUwwc(wn_VP`INSKly%kZa3z1Q-z zjcQD*IZBoq;S`4k(O^&GIk~q>C#qJf6P^>-p`d1m%g6b??er(NE@#`)M7y@_kAAKX zJTTZE+x?t9x)G07#6FsZwVNWHilmNnM{+$FIfze_K? z!JvofoOVXLEer@ag&(@tBL-KMQ zD_)(Sm};XBX9<|-9@N~F5N6fCu-!62TE93ywarlctYF_%s}zt;g8$B5ql@6^{g*69rNG(f4Tb>GX+shpD+>`U+;R{L^a{aN>qtczH$ z6}f7`hZhpDmA?KL$u0`K_UYqdAqka4< zbH#PM^b2L>7){HEXDO?~iG0H9oo<{%@lu=DsqJ3{({!b-w7(u92)h9?zv~>fc}hMJ z-V>e3qkLAW+a0`L*{~Jp1Q)f=ThbdXz_b6*Iq`Vkbj3bp;ewJcwE3|%`*(I@k8=+n z4T>cVG;z8PFAVK{YPLHtn48L?kuynpd~%ennQg(Ql->m`jj+{w%Y;k$r23OUtjzv}^J6pao;T^}k)1U(LXl(*h*+fXdjVh}n(Spg1|C1^>H zxOC1qqN+2%B%)U=%Nn$(-O zW0f|vSmF$KH;{cKmerc!X|PVydsv;2lchxDU%XNOsYcIotAn<7Cnlj7R4-f#8?nla z)TN4RLwSN??yfanT+cgaIN+<#RpE3RjyM6R8`!Z zK1Fw>=A{3DLlQ@kPm}>Ehd0xZ@=$ zURtTI;SLV8+n#>^W%u3PDuJx^zTh-*fLzR;4a p8`e-4X+W?oKh{q)ty(-_yc}Lsu608?h { + + private final ExecutorService processingExecutorService = Executors.newSingleThreadExecutor(); + + private final BlockingQueue batchingQueue; + private final List processingQueue; + private final int batchSize; + private final Consumer> itemsConsumer; + private final long millisTimeout; + + private Future lastProcessingFuture = CompletableFuture.runAsync(() -> { + }); + + public BatchProcessor(int batchSize, long millisTimeout, Consumer> itemsConsumer) { + this.batchingQueue = new LinkedBlockingQueue<>(batchSize); + this.processingQueue = new ArrayList<>(batchSize); + this.batchSize = batchSize; + this.itemsConsumer = itemsConsumer; + this.millisTimeout = millisTimeout; + } + + public synchronized void clearCurrentBatch() { + batchingQueue.clear(); + } + + public synchronized void flush(boolean blockUntilFinished) throws InterruptedException { + asyncProcessBatch(); + if (blockUntilFinished) { + lastProcessingFuture.wait(millisTimeout); + } + } + + public synchronized void add(T item) throws InterruptedException { + batchingQueue.add(item); + if (batchingQueue.size() >= batchSize) { + asyncProcessBatch(); + } + } + + private synchronized void asyncProcessBatch() throws InterruptedException { + if (!batchingQueue.isEmpty()) { + // wait for previous processing to finish + lastProcessingFuture.wait(millisTimeout); + + // if 'andThen' doesn't run, clear the processing queue + processingQueue.clear(); + + // transfer batching queue to processing queue + batchingQueue.drainTo(processingQueue); + + // submit to processor and then clear processing queue + lastProcessingFuture = processingExecutorService.submit( + () -> itemsConsumer.andThen(processingQueue -> processingQueue.clear()).accept(processingQueue) + ); + } + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED new file mode 100644 index 0000000000..9b715cf1fb --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED @@ -0,0 +1,23 @@ +MalwareScanIngestModule_malwareTypeDisplayName=Malware +# {0} - errorResponse +MalwareScanIngestModule_SharedProcessing_authTokenResponseError_desc=Received error: ''{0}'' when fetching the API authentication token for the license +MalwareScanIngestModule_SharedProcessing_authTokenResponseError_title=Authentication API error +MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No=NO +MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes=YES +MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc=The remaining hash lookups for this license have been exhausted +MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title=Hash Lookups Exhausted +MalwareScanIngestModule_SharedProcessing_flushTimeout_desc=A timeout occurred while finishing processing +MalwareScanIngestModule_SharedProcessing_flushTimeout_title=Processing Timeout +MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc=An error occurred while processing hash lookup results +MalwareScanIngestModule_SharedProcessing_generalProcessingError_title=Hash Lookup Error +# {0} - errorResponse +MalwareScanIngestModule_SharedProcessing_repServicenResponseError_desc=Received error: ''{0}'' when fetching hash lookup results +MalwareScanIngestModule_SharedProcessing_repServicenResponseError_title=Lookup API error +MalwareScanIngestModule_ShareProcessing_batchTimeout_desc=Batch processing timed out +MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Timeout +# {0} - remainingLookups +MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining +MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low +MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the CyberTriage cloud API for any possible malicious executables. +MalwareScanIngestModuleFactory_displayName=Malware Scan +MalwareScanIngestModuleFactory_version=1.0.0 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java new file mode 100644 index 0000000000..2b049124bf --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -0,0 +1,395 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.malwarescan; + +import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; +import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; +import com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud.CTLicensePersistence; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestModule; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskData; + +/** + * Uses CT cloud API to determine if file is malware + */ +public class MalwareScanIngestModule implements FileIngestModule { + + private static final SharedProcessing sharedProcessing = new SharedProcessing(); + + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + sharedProcessing.startUp(context); + } + + @Override + public ProcessResult process(AbstractFile af) { + return sharedProcessing.process(af); + } + + @Override + public void shutDown() { + sharedProcessing.shutDown(); + } + + /** + * Does the bulk of processing for the ingest module and handles concurrent + * ingest modules adding files simultaneously. + */ + private static class SharedProcessing { + + // batch size of 200 files max + private static final int BATCH_SIZE = 200; + // 3 minute timeout for an API request + private static final long BATCH_MILLIS_TIMEOUT = 3 * 60 * 1000; + + //minimum lookups left before issuing warning + private static final long LOW_LOOKUPS_REMAINING = 250; + + private static final Set EXECUTABLE_MIME_TYPES = Stream.of( + "application/x-bat",//NON-NLS + "application/x-dosexec",//NON-NLS + "application/vnd.microsoft.portable-executable",//NON-NLS + "application/x-msdownload",//NON-NLS + "application/exe",//NON-NLS + "application/x-exe",//NON-NLS + "application/dos-exe",//NON-NLS + "vms/exe",//NON-NLS + "application/x-winexe",//NON-NLS + "application/msdos-windows",//NON-NLS + "application/x-msdos-program"//NON-NLS + ).collect(Collectors.toSet()); + + private static final String MALWARE_TYPE_NAME = "TSK_MALWARE"; + private static final String MALWARE_CONFIG = "Cyber Triage Cloud"; + + private static final Logger logger = Logger.getLogger(MalwareScanIngestModule.class.getName()); + private final BatchProcessor batchProcessor = new BatchProcessor(BATCH_SIZE, BATCH_MILLIS_TIMEOUT, this::handleBatch); + + private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance(); + private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); + + private FileTypeDetector fileTypeDetector; + private RunState runState = null; + private SleuthkitCase tskCase = null; + private LicenseInfo licenseInfo = null; + private BlackboardArtifact.Type malwareType = null; + private boolean noMoreHashLookups = false; + private IngestModuleException startupException; + private long dsId = 0; + + @Messages({ + "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low", + "# {0} - remainingLookups", + "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining", + "MalwareScanIngestModule_malwareTypeDisplayName=Malware" + }) + synchronized void startUp(IngestJobContext context) throws IngestModuleException { + // only run this code once per startup + if (runState == RunState.STARTED_UP) { + if (startupException != null) { + throw startupException; + } else { + return; + } + } + + try { + // get saved license + Optional licenseInfoOpt = ctSettingsPersistence.loadLicenseInfo(); + if (licenseInfoOpt.isEmpty() || licenseInfoOpt.get().getDecryptedLicense() == null) { + throw new IngestModuleException("No saved license was found"); + } + + String licenseStr = licenseInfoOpt.get().getDecryptedLicense().getBoostLicenseId(); + AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseStr); + // syncronously fetch malware scans info + + // determine lookups remaining + long lookupsRemaining = remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount()); + if (lookupsRemaining <= 0) { + throw new IngestModuleException("There are no more file hash lookups for this license"); + } else if (lookupsRemaining < LOW_LOOKUPS_REMAINING) { + notifyWarning( + Bundle.MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title(), + Bundle.MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc(lookupsRemaining), + null); + } + + // setup necessary variables for processing + tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + malwareType = tskCase.getBlackboard().getOrAddArtifactType( + MALWARE_TYPE_NAME, + Bundle.MalwareScanIngestModule_malwareTypeDisplayName(), + BlackboardArtifact.Category.ANALYSIS_RESULT); + fileTypeDetector = new FileTypeDetector(); + dsId = context.getDataSource().getId(); + licenseInfo = licenseInfoOpt.get(); + startupException = null; + noMoreHashLookups = false; + } catch (IngestModuleException ex) { + startupException = ex; + throw startupException; + } catch (Exception ex) { + startupException = new IngestModuleException("An exception occurred on MalwareScanIngestModule startup", ex); + throw startupException; + } + } + + private static long remaining(Long limit, Long used) { + limit = limit == null ? 0 : limit; + used = used == null ? 0 : used; + return limit - used; + } + + @Messages({ + "MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Timeout", + "MalwareScanIngestModule_ShareProcessing_batchTimeout_desc=Batch processing timed out" + }) + IngestModule.ProcessResult process(AbstractFile af) { + try { + if (af.getKnown() != TskData.FileKnown.KNOWN + && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.getMIMEType(af)).trim().toLowerCase())) { + batchProcessor.add(new FileRecord(af.getId(), af.getMd5Hash())); + + } + return ProcessResult.OK; + } catch (InterruptedException ex) { + notifyWarning( + Bundle.MalwareScanIngestModule_ShareProcessing_batchTimeout_title(), + Bundle.MalwareScanIngestModule_ShareProcessing_batchTimeout_desc(), + ex); + return IngestModule.ProcessResult.ERROR; + } + } + + @Messages({ + "MalwareScanIngestModule_SharedProcessing_authTokenResponseError_title=Authentication API error", + "# {0} - errorResponse", + "MalwareScanIngestModule_SharedProcessing_authTokenResponseError_desc=Received error: ''{0}'' when fetching the API authentication token for the license", + "MalwareScanIngestModule_SharedProcessing_repServicenResponseError_title=Lookup API error", + "# {0} - errorResponse", + "MalwareScanIngestModule_SharedProcessing_repServicenResponseError_desc=Received error: ''{0}'' when fetching hash lookup results", + "MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title=Hash Lookups Exhausted", + "MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc=The remaining hash lookups for this license have been exhausted", + "MalwareScanIngestModule_SharedProcessing_generalProcessingError_title=Hash Lookup Error", + "MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc=An error occurred while processing hash lookup results",}) + private void handleBatch(List fileRecords) { + if (fileRecords == null || fileRecords.isEmpty() || noMoreHashLookups) { + return; + } + + // create mapping of md5 to corresponding object ids as well as just the list of md5's + Map> md5ToObjId = new HashMap<>(); + List md5Hashes = new ArrayList<>(); + for (FileRecord fr : fileRecords) { + if (fr == null || StringUtils.isBlank(fr.getMd5hash()) || fr.getObjId() <= 0) { + continue; + } + + String sanitizedMd5 = sanitizedMd5(fr.getMd5hash()); + md5ToObjId + .computeIfAbsent(sanitizedMd5, (k) -> new ArrayList<>()) + .add(fr.getObjId()); + + md5Hashes.add(sanitizedMd5); + } + + if (md5Hashes.isEmpty()) { + return; + } + + try { + // get an auth token with the license + AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseInfo.getDecryptedLicense().getBoostLicenseId()); + + // make sure we are in bounds for the remaining scans + long remainingScans = remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount()); + if (remainingScans <= 0) { + noMoreHashLookups = true; + notifyWarning( + Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(), + Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(), + null); + return; + } + + // if the size of this batch will exceed limit, shrink list to limit and fail after processing + boolean exceededScanLimit = false; + if (remainingScans < md5Hashes.size()) { + md5Hashes = md5Hashes.subList(0, (int) remainingScans); + exceededScanLimit = true; + } + + // using auth token, get results + List repResult = ctApiDAO.getReputationResults(authTokenResponse.getToken(), md5Hashes); + + if (repResult != null && !repResult.isEmpty()) { + SleuthkitCase.CaseDbTransaction trans = null; + try { + trans = tskCase.beginTransaction(); + for (FileReputationResult result : repResult) { + String sanitizedMd5 = sanitizedMd5(result.getMd5Hash()); + List objIds = md5ToObjId.remove(sanitizedMd5); + if (objIds == null || objIds.isEmpty()) { + continue; + } + + for (Long objId : objIds) { + createAnalysisResult(objId, result, trans); + } + } + + trans.commit(); + trans = null; + } finally { + if (trans != null) { + trans.rollback(); + trans = null; + } + } + + // if we only processed part of the batch, after processing, notify that we are out of scans. + if (exceededScanLimit) { + noMoreHashLookups = true; + notifyWarning( + Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(), + Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(), + null); + return; + } + } + } catch (Exception ex) { + notifyWarning( + Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_title(), + Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc(), + ex); + } + } + + private String sanitizedMd5(String orig) { + return StringUtils.defaultString(orig).trim().toLowerCase(); + } + + @Messages({ + "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes=YES", + "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No=NO" + }) + private void createAnalysisResult(Long objId, FileReputationResult fileReputationResult, SleuthkitCase.CaseDbTransaction trans) throws Blackboard.BlackboardException { + if (objId == null || fileReputationResult == null) { + return; + } + + Score score = fileReputationResult.getScore() == null ? Score.SCORE_UNKNOWN : fileReputationResult.getScore().getTskCore(); + + String conclusion = score.getSignificance() == Score.Significance.NOTABLE || score.getSignificance() == Score.Significance.LIKELY_NOTABLE + ? Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes() + : Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No(); + + String justification = fileReputationResult.getStatusDescription(); + + tskCase.getBlackboard().newAnalysisResult( + malwareType, + objId, + dsId, + score, + conclusion, + MALWARE_CONFIG, + justification, + Collections.emptyList(), + trans); + } + + @Messages({ + "MalwareScanIngestModule_SharedProcessing_flushTimeout_title=Processing Timeout", + "MalwareScanIngestModule_SharedProcessing_flushTimeout_desc=A timeout occurred while finishing processing" + }) + synchronized void shutDown() { + // if already shut down, return + if (runState == RunState.SHUT_DOWN) { + return; + } + + // flush any remaining items + try { + batchProcessor.flush(true); + } catch (InterruptedException ex) { + notifyWarning( + Bundle.MalwareScanIngestModule_SharedProcessing_flushTimeout_title(), + Bundle.MalwareScanIngestModule_SharedProcessing_flushTimeout_desc(), + ex); + } finally { + // set state to shut down and clear any remaining + malwareType = null; + fileTypeDetector = null; + noMoreHashLookups = false; + runState = RunState.SHUT_DOWN; + startupException = null; + batchProcessor.clearCurrentBatch(); + } + } + + private void notifyWarning(String title, String message, Exception ex) { + MessageNotifyUtil.Notify.warn(title, message); + logger.log(Level.WARNING, message, ex); + } + + private enum RunState { + STARTED_UP, SHUT_DOWN + } + + class FileRecord { + + private final long objId; + private final String md5hash; + + FileRecord(long objId, String md5hash) { + this.objId = objId; + this.md5hash = md5hash; + } + + long getObjId() { + return objId; + } + + String getMd5hash() { + return md5hash; + } + + } + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java new file mode 100644 index 0000000000..0a7fffb416 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java @@ -0,0 +1,72 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.malwarescan; + +import com.basistech.df.cybertriage.autopsy.ctoptions.CTOptionsPanel; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * Factory for malware scan ingest modules. + */ +@ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) +@Messages({ + "MalwareScanIngestModuleFactory_displayName=Malware Scan", + "MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the CyberTriage cloud API for any possible malicious executables.", + "MalwareScanIngestModuleFactory_version=1.0.0" +}) +public class MalwareScanIngestModuleFactory extends IngestModuleFactoryAdapter { + + @Override + public String getModuleDisplayName() { + return Bundle.MalwareScanIngestModuleFactory_displayName(); + } + + @Override + public String getModuleDescription() { + return Bundle.MalwareScanIngestModuleFactory_description(); + } + + @Override + public String getModuleVersionNumber() { + return Bundle.MalwareScanIngestModuleFactory_version(); + } + + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) { + return new MalwareScanIngestModule(); + } + + @Override + public boolean hasGlobalSettingsPanel() { + return true; + } + + @Override + public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() { + CTOptionsPanel optionsPanel = new CTOptionsPanel(); + optionsPanel.loadSavedSettings(); + return optionsPanel; + } + +} diff --git a/CoreLibs/ivy.xml b/CoreLibs/ivy.xml index 51bbeb8220..d60ce17600 100644 --- a/CoreLibs/ivy.xml +++ b/CoreLibs/ivy.xml @@ -1,5 +1,6 @@ + ]> @@ -87,7 +88,8 @@ - + + @@ -142,8 +144,8 @@ - - + + diff --git a/CoreLibs/manifest.mf b/CoreLibs/manifest.mf index b8faef2527..1d3168bf2c 100644 --- a/CoreLibs/manifest.mf +++ b/CoreLibs/manifest.mf @@ -11,4 +11,4 @@ Specification-Version: 1.0 Specification-Vendor: CoreLibs ImageIO Fields Implementation-Title: org.sleuthkit.autopsy.corelibs.ImageIO Implementation-Version: 1.0 -Implementation-Vendor: CoreLibs ImageIO Fields \ No newline at end of file +Implementation-Vendor: CoreLibs ImageIO Fields diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties index fe5e78acff..320a1e36e5 100644 --- a/CoreLibs/nbproject/project.properties +++ b/CoreLibs/nbproject/project.properties @@ -84,6 +84,7 @@ file.reference.jackson-annotations-2.15.2.jar=release/modules/ext/jackson-annota file.reference.jackson-core-2.15.2.jar=release/modules/ext/jackson-core-2.15.2.jar file.reference.jackson-databind-2.15.2.jar=release/modules/ext/jackson-databind-2.15.2.jar file.reference.jackson-dataformat-csv-2.15.2.jar=release/modules/ext/jackson-dataformat-csv-2.15.2.jar +file.reference.jackson-datatype-jsr310-2.15.2.jar=release/modules/ext/jackson-datatype-jsr310-2.15.2.jar file.reference.javafx-base-17.0.7-linux.jar=release/modules/ext/javafx-base-17.0.7-linux.jar file.reference.javafx-base-17.0.7-mac.jar=release/modules/ext/javafx-base-17.0.7-mac.jar file.reference.javafx-base-17.0.7-win.jar=release/modules/ext/javafx-base-17.0.7-win.jar diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index f7b139f502..b2f08dc42b 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -63,6 +63,8 @@ com.fasterxml.jackson.databind.type com.fasterxml.jackson.databind.util com.fasterxml.jackson.dataformat.csv + com.fasterxml.jackson.datatype.jsr310 + com.fasterxml.jackson.datatype.jsr310.util com.github.lgooddatepicker.components com.github.lgooddatepicker.optionalusertools com.github.lgooddatepicker.zinternaltools @@ -197,8 +199,6 @@ com.google.rpc.context com.google.thirdparty.publicsuffix com.google.type - com.microsoft.schemas.vml - com.microsoft.schemas.vml.impl com.sun.javafx com.sun.javafx.animation com.sun.javafx.application @@ -321,9 +321,6 @@ com.twelvemonkeys.util.regex com.twelvemonkeys.util.service com.twelvemonkeys.xml - javax.annotation - javax.annotation.concurrent - javax.annotation.meta javafx.animation javafx.application javafx.beans @@ -340,7 +337,6 @@ javafx.event javafx.fxml javafx.geometry - javafx.graphics javafx.print javafx.scene javafx.scene.canvas @@ -362,19 +358,9 @@ javafx.stage javafx.util javafx.util.converter - javax.jms - javax.mail - javax.mail.event - javax.mail.internet - javax.mail.search - javax.mail.util - javax.servlet - javax.servlet.http - javax.xml.parsers - javax.xml.transform - javax.xml.transform.dom - javax.xml.transform.sax - javax.xml.transform.stream + javax.annotation + javax.annotation.concurrent + javax.annotation.meta jfxtras.animation jfxtras.css jfxtras.css.converters @@ -442,16 +428,6 @@ org.apache.commons.lang3.tuple org.apache.commons.logging org.apache.commons.logging.impl - org.apache.log - org.apache.log.filter - org.apache.log.format - org.apache.log.output - org.apache.log.output.db - org.apache.log.output.io - org.apache.log.output.io.rotate - org.apache.log.output.jms - org.apache.log.output.net - org.apache.log.util org.apache.commons.text org.apache.commons.validator.routines org.apache.commons.validator.routines.checkdigit @@ -460,14 +436,7 @@ org.apache.log4j.config org.apache.log4j.helpers org.apache.log4j.jdbc - org.apache.log4j.jmx - org.apache.log4j.lf5 - org.apache.log4j.lf5.util - org.apache.log4j.lf5.viewer - org.apache.log4j.lf5.viewer.categoryexplorer - org.apache.log4j.lf5.viewer.configure org.apache.log4j.net - org.apache.log4j.nt org.apache.log4j.or org.apache.log4j.or.jms org.apache.log4j.or.sax @@ -931,6 +900,10 @@ ext/jackson-dataformat-csv-2.15.2.jar release/modules/ext/jackson-dataformat-csv-2.15.2.jar
+ + ext/jackson-datatype-jsr310-2.15.2.jar + release/modules/ext/jackson-datatype-jsr310-2.15.2.jar + ext/javafx-base-17.0.7-linux.jar release/modules/ext/javafx-base-17.0.7-linux.jar diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 6d7443a4a6..f8cea13909 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Wed, 28 Sep 2022 13:57:05 -0400 +#Thu, 20 Jul 2023 14:02:30 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 @@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18 SplashRunningTextColor=0x0 SplashRunningTextFontSize=19 -currentVersion=Autopsy 4.19.3 +currentVersion=Autopsy 4.20.0 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index f28a6b96b3..2387b67597 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Wed, 28 Sep 2022 13:57:05 -0400 -CTL_MainWindow_Title=Autopsy 4.19.3 -CTL_MainWindow_Title_No_Project=Autopsy 4.19.3 +#Thu, 20 Jul 2023 14:02:30 -0400 +CTL_MainWindow_Title=Autopsy 4.20.0 +CTL_MainWindow_Title_No_Project=Autopsy 4.20.0 From 38ad881023f13b69b05c167dbdc6faafeef62d27 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 20 Jul 2023 16:07:59 -0400 Subject: [PATCH 02/29] cloud http client with proxy --- .../autopsy/ctapi/CTCloudHttpClient.java | 378 ++++++++++++++++++ .../cybertriage/autopsy/ctapi/CtApiDAO.java | 53 ++- .../autopsy/ctapi/json/AuthTokenResponse.java | 12 +- .../ctapi/json/AuthenticatedRequestData.java | 36 ++ .../ctapi/json/FileReputationRequest.java | 58 +++ .../ctapi/json/FileReputationResponse.java | 38 ++ .../ctapi/util/CTHostIDGenerationUtil.java | 25 +- .../ctcloud/CTMalwareScannerOptionsPanel.java | 4 +- .../malwarescan/MalwareScanIngestModule.java | 6 +- 9 files changed, 565 insertions(+), 45 deletions(-) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java new file mode 100644 index 0000000000..8d04c4c6d6 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -0,0 +1,378 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi; + +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.logging.Level; +import javax.net.ssl.SSLContext; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.NTCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.AuthSchemes; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.client.SystemDefaultCredentialsProvider; +import org.apache.http.impl.client.WinHttpClients; + +/** + * Actually makes the http requests to CT cloud. + */ +public class CTCloudHttpClient { + + private static final CTCloudHttpClient instance = new CTCloudHttpClient(); + private static final Logger LOGGER = Logger.getLogger(CTCloudHttpClient.class.getName()); + + private static final List DEFAULT_SCHEME_PRIORITY + = new ArrayList<>(Arrays.asList( + AuthSchemes.SPNEGO, + AuthSchemes.KERBEROS, + AuthSchemes.NTLM, + AuthSchemes.CREDSSP, + AuthSchemes.DIGEST, + AuthSchemes.BASIC)); + + private static final int CONNECTION_TIMEOUT_MS = 58 * 1000; // milli sec + + public static CTCloudHttpClient getInstance() { + return instance; + } + + private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper(); + private final SSLContext sslContext; + private String hostName = null; + + private CTCloudHttpClient() { + SSLContext tmpSslContext; + try { + tmpSslContext = SSLContext.getInstance("TLSv1.2"); + } catch (NoSuchAlgorithmException ex) { + LOGGER.log(Level.WARNING, "Unable to setup ssl context instance", ex); + tmpSslContext = null; + } + this.sslContext = tmpSslContext; + } + + private ProxySettingArgs getProxySettings() { + if (StringUtils.isBlank(hostName)) { + try { + hostName = InetAddress.getLocalHost().getCanonicalHostName(); + } catch (UnknownHostException ex) { + LOGGER.log(Level.WARNING, "An error occurred while fetching the hostname", ex); + } + } + + int proxyPort = 0; + try { + proxyPort = Integer.parseInt(ProxySettings.getHttpsPort()); + } catch (NumberFormatException ex) { + LOGGER.log(Level.WARNING, "An exception occurred while converting port number to integer", ex); + } + + return new ProxySettingArgs( + ProxySettings.getProxyType() != ProxySettings.DIRECT_CONNECTION, + hostName, + ProxySettings.getHttpsHost(), + proxyPort, + ProxySettings.getAuthenticationUsername(), + ProxySettings.getAuthenticationPassword(), + null + ); + } + + public O doPost(String urlPath, Object jsonBody, Class classType) throws CTCloudException { + String url = Constants.CT_CLOUD_SERVER + urlPath; + try { + + LOGGER.log(Level.INFO, "initiating http connection to ctcloud server"); + try (CloseableHttpClient httpclient = createConnection(getProxySettings(), sslContext)) { + URIBuilder builder = new URIBuilder(url); + URI postURI = builder.build(); + HttpPost postRequest = new HttpPost(postURI); + + configureRequestTimeout(postRequest); + postRequest.setHeader("Content-type", "application/json"); + + if (jsonBody != null) { + String requestBody = mapper.writeValueAsString(jsonBody); + if (StringUtils.isNotBlank(requestBody)) { + HttpEntity entity = new StringEntity(requestBody, "UTF-8"); + postRequest.setEntity(entity); + } + } + + LOGGER.log(Level.INFO, "initiating http post request to ctcloud server " + postRequest.getURI()); + try (CloseableHttpResponse response = httpclient.execute(postRequest)) { + + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + LOGGER.log(Level.INFO, "Response Received. - Status OK"); + // Parse Response + HttpEntity entity = response.getEntity(); + String entityStr = EntityUtils.toString(entity); + O respObj = mapper.readValue(entityStr, classType); + return respObj; + } else { + LOGGER.log(Level.WARNING, "Response Received. - Status Error {}", response.getStatusLine()); + handleNonOKResponse(response, ""); + } + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Error when parsing response from CyberTriage Cloud", ex); + throw new CTCloudException(CTCloudException.parseUnknownException(ex), ex); + } + } + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "IO Exception raised when connecting to CT Cloud using " + url, ex); + throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex); + } catch (URISyntaxException ex) { + LOGGER.log(Level.WARNING, "Wrong URL syntax for CT Cloud " + url, ex); + throw new CTCloudException(CTCloudException.ErrorCode.UNKNOWN, ex); + } + + return null; + } + + /** + * A generic way to handle the HTTP response - when the response code is NOT + * 200 OK. + * + * @param response + * @param fileName - used only for logging. + * @throws MalwareScannerException + * @throws IOException + */ + private void handleNonOKResponse(CloseableHttpResponse response, String fileName) throws CTCloudException, IOException { + LOGGER.log(Level.WARNING, MessageFormat.format( + "Response code {0}. Message Body {1}", + response.getStatusLine().getStatusCode(), + EntityUtils.toString(response.getEntity()))); + + switch (response.getStatusLine().getStatusCode()) { + + case HttpStatus.SC_BAD_REQUEST: + //400: Bad request => Unsupported HTTP method or invalid http request (e.g., empty body). + throw new CTCloudException(CTCloudException.ErrorCode.BAD_REQUEST); + case HttpStatus.SC_UNAUTHORIZED: + //401 Invalid API key => An invalid API key, or no API key, has been provided + throw new CTCloudException(CTCloudException.ErrorCode.INVALID_KEY); + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + // 407 Proxy server authentication required. + throw new CTCloudException(CTCloudException.ErrorCode.PROXY_UNAUTHORIZED); + case HttpStatus.SC_FORBIDDEN: + throw new CTCloudException(CTCloudException.ErrorCode.UN_AUTHORIZED); + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + //500 Internal error Server temporarily unavailable; please try again later. If the issue persists, please contact RL. + throw new CTCloudException(CTCloudException.ErrorCode.TEMP_UNAVAILABLE); + case HttpStatus.SC_SERVICE_UNAVAILABLE: + //503 Server is too busy. Try again later. + //503 Failed to request scan. Try again later. The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. If the issue persists, please contact RL. + throw new CTCloudException(CTCloudException.ErrorCode.TEMP_UNAVAILABLE); + case HttpStatus.SC_GATEWAY_TIMEOUT: + throw new CTCloudException(CTCloudException.ErrorCode.GATEWAY_TIMEOUT); + default: + String returnData = EntityUtils.toString(response.getEntity()); + LOGGER.log(Level.WARNING, MessageFormat.format("upload response content for {0}:\n {1}", fileName, returnData)); + throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR); + } + } + + /** + * NOTE That this is not a perfect solution as timeouts set this way does + * not terminate a connection forcefully after a specified interval. so if + * there is data streaming in from the server at a small speed the + * connection will be kept open. + * + * @param request + */ + private void configureRequestTimeout(HttpRequestBase request) { + RequestConfig config = RequestConfig.custom() + .setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS) + .setConnectTimeout(CONNECTION_TIMEOUT_MS) + .setSocketTimeout(CONNECTION_TIMEOUT_MS) + .build(); + request.setConfig(config); + } + + /** + * Creates and returns a CloseableHttpClient SYSTEM and MANUAL looks up from + * runtime proxy config settings. These are updated accordingly from the + * Proxy Config UI. This allows us to keep the CreateConnection call fairly + * simple and not have to deal with the System Proxy settings and such. + * + * @return + */ + private static CloseableHttpClient createConnection(ProxySettingArgs proxySettings, SSLContext sslContext) { + HttpClientBuilder builder = getHttpClientBuilder(proxySettings); + + if (sslContext != null) { + builder.setSSLContext(sslContext); + } + return builder.build(); + } + + private static HttpClientBuilder getHttpClientBuilder(ProxySettingArgs proxySettings) { + + if (proxySettings.isSystemOrManualProxy()) { + + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + LOGGER.info("Requesting Password Authentication..."); + return super.getPasswordAuthentication(); + } + }); + + HttpClientBuilder builder = null; + HttpHost proxyHost = null; + CredentialsProvider proxyCredsProvider = null; + RequestConfig config = null; + + if (Objects.nonNull(proxySettings.getProxyHostname()) && proxySettings.getProxyPort() > 0) { + proxyHost = new HttpHost(proxySettings.getProxyHostname(), proxySettings.getProxyPort()); + + proxyCredsProvider = getProxyCredentialsProvider(proxySettings); + if (StringUtils.isNotBlank(proxySettings.getAuthScheme())) { + if (!DEFAULT_SCHEME_PRIORITY.get(0).equalsIgnoreCase(proxySettings.getAuthScheme())) { + DEFAULT_SCHEME_PRIORITY.removeIf(s -> s.equalsIgnoreCase(proxySettings.getAuthScheme())); + DEFAULT_SCHEME_PRIORITY.add(0, proxySettings.getAuthScheme()); + } + } + config = RequestConfig.custom().setProxyPreferredAuthSchemes(DEFAULT_SCHEME_PRIORITY).build(); + } + + if (Objects.isNull(proxyCredsProvider) && WinHttpClients.isWinAuthAvailable()) { + builder = WinHttpClients.custom(); + builder.useSystemProperties(); + LOGGER.log(Level.WARNING, "Using Win HTTP Client"); + } else { + builder = HttpClients.custom(); + builder.setDefaultRequestConfig(config); + if (Objects.nonNull(proxyCredsProvider)) { // make sure non null proxycreds before setting it + builder.setDefaultCredentialsProvider(proxyCredsProvider); + } + LOGGER.log(Level.WARNING, "Using default http client"); + } + if (Objects.nonNull(proxyHost)) { + builder.setProxy(proxyHost); + LOGGER.log(Level.WARNING, MessageFormat.format("Using proxy {0}", proxyHost)); + } + + return builder; + } else { + return HttpClients.custom(); + } + } + + /** + * Returns a CredentialsProvider for proxy, if one is configured. + * + * @return CredentialsProvider, if a proxy is configured with credentials, + * null otherwise + */ + private static CredentialsProvider getProxyCredentialsProvider(ProxySettingArgs proxySettings) { + CredentialsProvider proxyCredsProvider = null; + if (proxySettings.isSystemOrManualProxy()) { + if (StringUtils.isNotBlank(proxySettings.getProxyUserId())) { + if (null != proxySettings.getProxyPassword() && proxySettings.getProxyPassword().length > 0) { // Password will be blank for KERBEROS / NEGOTIATE schemes. + proxyCredsProvider = new SystemDefaultCredentialsProvider(); + String userId = proxySettings.getProxyUserId(); + String domain = null; + if (userId.contains("\\")) { + domain = userId.split("\\\\")[0]; + userId = userId.split("\\\\")[1]; + } + String workStation = proxySettings.getHostName(); + proxyCredsProvider.setCredentials(new AuthScope(proxySettings.getProxyHostname(), proxySettings.getProxyPort()), + new NTCredentials(userId, new String(proxySettings.getProxyPassword()), workStation, domain)); + } + } + } + + return proxyCredsProvider; + } + + private static class ProxySettingArgs { + + private final boolean systemOrManualProxy; + private final String hostName; + private final String proxyHostname; + private final int proxyPort; + private final String proxyUserId; + private final char[] proxyPassword; + private final String authScheme; + + ProxySettingArgs(boolean systemOrManualProxy, String hostName, String proxyHostname, int proxyPort, String proxyUserId, char[] proxyPassword, String authScheme) { + this.systemOrManualProxy = systemOrManualProxy; + this.hostName = hostName; + this.proxyHostname = proxyHostname; + this.proxyPort = proxyPort; + this.proxyUserId = proxyUserId; + this.proxyPassword = proxyPassword; + this.authScheme = authScheme; + } + + boolean isSystemOrManualProxy() { + return systemOrManualProxy; + } + + String getHostName() { + return hostName; + } + + String getProxyHostname() { + return proxyHostname; + } + + int getProxyPort() { + return proxyPort; + } + + String getProxyUserId() { + return proxyUserId; + } + + char[] getProxyPassword() { + return proxyPassword; + } + + public String getAuthScheme() { + return authScheme; + } + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index 66fa03b8f1..f7b5e75147 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -15,50 +15,52 @@ package com.basistech.df.cybertriage.autopsy.ctapi; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthenticatedRequestData; +import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationRequest; +import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil; -import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Collections; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.sleuthkit.autopsy.coreutils.Version; /** * * Data access layer for handling the CT api. */ -public class CtApiDAO { +public class CTApiDAO { private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate"; private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token"; + private static final String CTCLOUD_SERVER_HASH_PATH = "/_ah/api/reputation/v1/query/file/hash/md5?query_types=CORRELATION,MALWARE"; - private static final CtApiDAO instance = new CtApiDAO(); - private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper(); + private static final CTApiDAO instance = new CTApiDAO(); + - private CtApiDAO() { + private CTApiDAO() { } - public static CtApiDAO getInstance() { + public static CTApiDAO getInstance() { return instance; } - + private static String getAppVersion() { return Version.getName() + " " + Version.getVersion(); } + + private final CTCloudHttpClient httpClient = CTCloudHttpClient.getInstance(); - private T doPost(String urlPath, Object requestBody, Class responseTypeRef) throws CTCloudException { - return null; - // TODO - } public LicenseResponse getLicenseInfo(String licenseString) throws CTCloudException { LicenseRequest licenseRequest = new LicenseRequest() .setBoostLicenseCode(licenseString) .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) .setProduct(getAppVersion()); - - return doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); + + return httpClient.doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); } @@ -67,14 +69,25 @@ public class CtApiDAO { .setAutopsyVersion(getAppVersion()) .setRequestFileUpload(true) .setBoostLicenseId(boostLicenseId); - - return doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); + + return httpClient.doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); } - public List getReputationResults(String authToken, List md5Hashes) throws CTCloudException { - // TODO -// return cloudServiceApi.lookupFileResults(md5Hashes, HashTypes.md5); - return null; + public List getReputationResults(AuthenticatedRequestData authenticatedRequestData, List md5Hashes) throws CTCloudException { + if (CollectionUtils.isEmpty(md5Hashes)) { + return Collections.emptyList(); + } + + FileReputationRequest fileRepReq = new FileReputationRequest() + .setApiKey(authenticatedRequestData.getApiKey()) + .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) + .setToken(authenticatedRequestData.getToken()) + .setHashes(md5Hashes); + + FileReputationResponse resp = httpClient.doPost(CTCLOUD_SERVER_HASH_PATH, fileRepReq, FileReputationResponse.class); + return resp == null || resp.getItems() == null + ? Collections.emptyList() + : resp.getItems(); } public enum ResultType { diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java index a010bbe13c..c936d05dbe 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -20,9 +20,7 @@ import java.time.ZonedDateTime; /** * POJO for an auth token response. */ -public class AuthTokenResponse { - private final String token; - private final String apiKey; +public class AuthTokenResponse extends AuthenticatedRequestData { private final Long hashLookupCount; private final Long hashLookupLimit; private final Long fileUploadLimit; @@ -51,14 +49,6 @@ public class AuthTokenResponse { this.expiration = expiration; } - public String getToken() { - return token; - } - - public String getApiKey() { - return apiKey; - } - public Long getHashLookupCount() { return hashLookupCount; } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java new file mode 100644 index 0000000000..34e0a61af7 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java @@ -0,0 +1,36 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Data required for an authenticated request. + */ +public abstract class AuthenticatedRequestData { + + @JsonProperty("token") + protected String token; + @JsonProperty("api_key") + protected String apiKey; + + public String getToken() { + return token; + } + + public String getApiKey() { + return apiKey; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java new file mode 100644 index 0000000000..0d256799f9 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java @@ -0,0 +1,58 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** + * Request for file reputation results. + */ +public class FileReputationRequest extends AuthenticatedRequestData { + + @JsonProperty("hashes") + private List hashes; + + @JsonProperty("host_id") + private String hostId; + + public List getHashes() { + return hashes; + } + + public FileReputationRequest setHashes(List hashes) { + this.hashes = hashes; + return this; + } + + public FileReputationRequest setToken(String token) { + this.token = token; + return this; + } + + public FileReputationRequest setApiKey(String apiKey) { + this.apiKey = apiKey; + return this; + } + + public String getHostId() { + return hostId; + } + + public FileReputationRequest setHostId(String hostId) { + this.hostId = hostId; + return this; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java new file mode 100644 index 0000000000..aaff89b1d1 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java @@ -0,0 +1,38 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** + * Container for file reputation result list response. + */ +public class FileReputationResponse { + + private final List items; + + @JsonCreator + public FileReputationResponse( + @JsonProperty("items") List items + ) { + this.items = items; + } + + public List getItems() { + return items; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java index f7b68f6bea..184e8593f9 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java @@ -29,7 +29,7 @@ public class CTHostIDGenerationUtil { private static final Logger LOGGER = Logger.getLogger(CTHostIDGenerationUtil.class.getName()); private static final String USER_NAME = System.getProperty("user.name"); - private static String HOST_NAME = ""; + private static String cachedId = ""; /** * Host ID Algorithm: Get MAC address from License4J. Get MD5 hash of it and @@ -41,17 +41,24 @@ public class CTHostIDGenerationUtil { * @return */ public static String generateLicenseHostID() { - if (StringUtils.isBlank(HOST_NAME)) { - + if (StringUtils.isBlank(cachedId)) { try { - HOST_NAME = StringUtils.defaultString(InetAddress.getLocalHost().getCanonicalHostName()); + String hostName = StringUtils.defaultString(InetAddress.getLocalHost().getCanonicalHostName()); + String macAddressMd5 = StringUtils.isNotBlank(HardwareID.getHardwareIDFromEthernetAddress()) + ? Md5HashUtil.getMD5MessageDigest(HardwareID.getHardwareIDFromEthernetAddress()).substring(0, 16) + : Md5HashUtil.getMD5MessageDigest(hostName).substring(0, 16); + + String usernameMd5 = StringUtils.isNotBlank(USER_NAME) + ? Md5HashUtil.getMD5MessageDigest(USER_NAME).substring(0, 16) + : Md5HashUtil.getMD5MessageDigest(hostName).substring(0, 16); + + cachedId = macAddressMd5 + "_" + usernameMd5; + } catch (UnknownHostException ex) { - LOGGER.log(Level.WARNING, "UNable to determine host name.", ex); + LOGGER.log(Level.WARNING, "Unable to determine host name.", ex); } } - String macAddressMd5 = StringUtils.isNotBlank(HardwareID.getHardwareIDFromEthernetAddress()) ? Md5HashUtil.getMD5MessageDigest(HardwareID.getHardwareIDFromEthernetAddress()).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16); - String usernameMd5 = StringUtils.isNotBlank(USER_NAME) ? Md5HashUtil.getMD5MessageDigest(USER_NAME).substring(0, 16) : Md5HashUtil.getMD5MessageDigest(HOST_NAME).substring(0, 16); - String md5 = macAddressMd5 + "_" + usernameMd5; - return md5; + + return cachedId; } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index 069d9fb653..6ff5e74edc 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -15,7 +15,7 @@ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; import com.basistech.df.cybertriage.autopsy.ctapi.CTCloudException; -import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; +import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; @@ -46,7 +46,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { private static final SimpleDateFormat LICENSE_EXPIRES_FORMAT = new SimpleDateFormat("MMMM d, YYYY"); private static final SimpleDateFormat MALWARE_SCANS_RESET_FORMAT = new SimpleDateFormat("MMM d, YYYY' at 'h:mma"); - private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); + private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance(); private volatile LicenseInfo licenseInfo = null; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index 2b049124bf..65a12f59b7 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -13,7 +13,7 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.malwarescan; -import com.basistech.df.cybertriage.autopsy.ctapi.CtApiDAO; +import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; @@ -101,7 +101,7 @@ public class MalwareScanIngestModule implements FileIngestModule { private final BatchProcessor batchProcessor = new BatchProcessor(BATCH_SIZE, BATCH_MILLIS_TIMEOUT, this::handleBatch); private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance(); - private final CtApiDAO ctApiDAO = CtApiDAO.getInstance(); + private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); private FileTypeDetector fileTypeDetector; private RunState runState = null; @@ -256,7 +256,7 @@ public class MalwareScanIngestModule implements FileIngestModule { } // using auth token, get results - List repResult = ctApiDAO.getReputationResults(authTokenResponse.getToken(), md5Hashes); + List repResult = ctApiDAO.getReputationResults(authTokenResponse, md5Hashes); if (repResult != null && !repResult.isEmpty()) { SleuthkitCase.CaseDbTransaction trans = null; From 541825472a7a460f30eab2478f17d2bef00b9f49 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 20 Jul 2023 21:45:39 -0400 Subject: [PATCH 03/29] some updates --- .../autopsy/ctapi/CTCloudHttpClient.java | 31 +++++++------ .../cybertriage/autopsy/ctapi/Constants.java | 10 ++--- .../cybertriage/autopsy/ctapi/CtApiDAO.java | 11 ++--- .../ctapi/json/DecryptedLicenseResponse.java | 17 +++++-- .../ctapi/util/LicenseDecryptorUtil.java | 4 +- .../autopsy/ctapi/util/ObjectMapperUtil.java | 45 ++++++++++++++++++- .../ctoptions/ctcloud/Bundle.properties | 3 +- .../ctcloud/Bundle.properties-MERGED | 3 +- .../ctoptions/ctcloud/CTLicenseDialog.java | 3 +- .../ctcloud/CTLicensePersistence.java | 3 +- .../ctcloud/CTMalwareScannerOptionsPanel.java | 11 +++-- CoreLibs/nbproject/project.xml | 4 ++ .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 14 files changed, 106 insertions(+), 43 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java index 8d04c4c6d6..93658c2057 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -22,14 +22,23 @@ import java.net.PasswordAuthentication; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.logging.Level; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -80,14 +89,8 @@ public class CTCloudHttpClient { private String hostName = null; private CTCloudHttpClient() { - SSLContext tmpSslContext; - try { - tmpSslContext = SSLContext.getInstance("TLSv1.2"); - } catch (NoSuchAlgorithmException ex) { - LOGGER.log(Level.WARNING, "Unable to setup ssl context instance", ex); - tmpSslContext = null; - } - this.sslContext = tmpSslContext; + // leave as null for now unless we want to customize this at a later date + this.sslContext = null; } private ProxySettingArgs getProxySettings() { @@ -100,12 +103,14 @@ public class CTCloudHttpClient { } int proxyPort = 0; - try { - proxyPort = Integer.parseInt(ProxySettings.getHttpsPort()); - } catch (NumberFormatException ex) { - LOGGER.log(Level.WARNING, "An exception occurred while converting port number to integer", ex); + if (StringUtils.isNotBlank(ProxySettings.getHttpPort())) { + try { + proxyPort = Integer.parseInt(ProxySettings.getHttpsPort()); + } catch (NumberFormatException ex) { + LOGGER.log(Level.WARNING, "Unable to convert port to integer"); + } } - + return new ProxySettingArgs( ProxySettings.getProxyType() != ProxySettings.DIRECT_CONNECTION, hostName, diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java index 3cc077c06d..29403ce7d4 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -13,12 +13,7 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi; -import com.google.common.collect.ImmutableList; import java.net.URI; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; // TODO take out anything sensitive or not used final public class Constants { @@ -31,10 +26,13 @@ final public class Constants { public static final String SSLTEST_URL = "https://www2.cybertriage.com/ssl_test.html"; - public static final String CT_CLOUD_SERVER = "https://rep1.cybertriage.com"; + public static final String CT_CLOUD_DEV_SERVER = "https://cyber-triage-dev.appspot.com"; + // TODO put back + public static final String CT_CLOUD_SERVER = CT_CLOUD_DEV_SERVER; //"https://rep1.cybertriage.com"; + /** * Link to watch demo video * @since 3.1.0 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index f7b5e75147..061f8a83e0 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -36,7 +36,8 @@ public class CTApiDAO { private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate"; private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token"; private static final String CTCLOUD_SERVER_HASH_PATH = "/_ah/api/reputation/v1/query/file/hash/md5?query_types=CORRELATION,MALWARE"; - + private static final String AUTOPSY_PRODUCT = "AUTOPSY"; + private static final CTApiDAO instance = new CTApiDAO(); @@ -48,7 +49,7 @@ public class CTApiDAO { } private static String getAppVersion() { - return Version.getName() + " " + Version.getVersion(); + return Version.getVersion(); } private final CTCloudHttpClient httpClient = CTCloudHttpClient.getInstance(); @@ -58,7 +59,7 @@ public class CTApiDAO { LicenseRequest licenseRequest = new LicenseRequest() .setBoostLicenseCode(licenseString) .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) - .setProduct(getAppVersion()); + .setProduct(AUTOPSY_PRODUCT); return httpClient.doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); @@ -89,8 +90,4 @@ public class CTApiDAO { ? Collections.emptyList() : resp.getItems(); } - - public enum ResultType { - OK, SERVER_ERROR, NOT_AUTHORIZED - } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index ec9e74a2ed..79ef4350d5 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -13,8 +13,12 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi.json; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.MDYDateDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.time.Instant; import java.time.ZonedDateTime; /** @@ -27,7 +31,7 @@ public class DecryptedLicenseResponse { private final ZonedDateTime expirationDate; private final Long hashLookups; private final Long fileUploads; - private final ZonedDateTime activationTime; + private final Instant activationTime; private final String product; private final String limitType; @@ -35,12 +39,17 @@ public class DecryptedLicenseResponse { public DecryptedLicenseResponse( @JsonProperty("boostLicenseId") String boostLicenseId, @JsonProperty("licenseHostId") String licenseHostId, + @JsonDeserialize(using=MDYDateDeserializer.class) @JsonProperty("expirationDate") ZonedDateTime expirationDate, @JsonProperty("hashLookups") Long hashLookups, @JsonProperty("fileUploads") Long fileUploads, - @JsonProperty("activationTime") ZonedDateTime activationTime, + @JsonDeserialize(using=InstantEpochMillisDeserializer.class) + @JsonProperty("activationTime") Instant activationTime, @JsonProperty("product") String product, - @JsonProperty("limitType") String limitType + @JsonProperty("limitType") String limitType, + + @JsonProperty("l4jLicenseId") String l4jlicenseId, + @JsonProperty("ctLicenseId") String ctLicenseId ) { this.boostLicenseId = boostLicenseId; this.licenseHostId = licenseHostId; @@ -68,7 +77,7 @@ public class DecryptedLicenseResponse { return fileUploads; } - public ZonedDateTime getActivationTime() { + public Instant getActivationTime() { return activationTime; } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java index efa91afd8d..8276b8a55a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java @@ -87,9 +87,9 @@ public class LicenseDecryptorUtil { } DecryptedLicenseResponse decryptedLicense = objectMapper.readValue(decryptedJsonResponse, DecryptedLicenseResponse.class); - if (!"CYBERTRIAGE".equalsIgnoreCase(decryptedLicense.getProduct())) { + if (!"AUTOPSY".equalsIgnoreCase(decryptedLicense.getProduct())) { // license file is expected to contain product of "CYBERTRIAGE" - throw new InvalidLicenseException("Not a valid Cyber Triage license"); + throw new InvalidLicenseException("Not a valid Autopsy license"); } return decryptedLicense; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java index 79098ce650..4a5ecd7925 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -13,8 +13,19 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi.util; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; /** * Creates default ObjectMapper @@ -33,9 +44,41 @@ public class ObjectMapperUtil { public ObjectMapper getDefaultObjectMapper() { ObjectMapper defaultMapper = new ObjectMapper(); - defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); +// defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); defaultMapper.registerModule(new JavaTimeModule()); return defaultMapper; } + public static class MDYDateDeserializer extends JsonDeserializer { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("MMM dd, yyyy"); + @Override + public ZonedDateTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { + JsonNode node = jp.getCodec().readTree(jp); + String nodeText = node.asText(); + try { + return ZonedDateTime.parse(nodeText, FORMATTER); + } catch (DateTimeParseException ex) { + return null; + } + } + + } + + public static class InstantEpochMillisDeserializer extends JsonDeserializer { + + @Override + public Instant deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { + JsonNode node = jp.getCodec().readTree(jp); + if (!node.isNumber()) { + return null; + } else { + try { + long millis = node.asLong(); + return Instant.ofEpochMilli(millis); + } catch (DateTimeException ex) { + return null; + } + } + } + } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties index 5df431e10c..9b875b95c9 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties @@ -2,7 +2,7 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template - +CTLicenseDialog.title=Add a License... CTLicenseDialog.licenseNumberLabel.text=License Number: CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX CTLicenseDialog.cancelButton.text=Cancel @@ -21,3 +21,4 @@ CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED index 6eaa730072..4819107a24 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED @@ -2,7 +2,7 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template - +CTLicenseDialog.title=Add a License... CTLicenseDialog.licenseNumberLabel.text=License Number: CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX CTLicenseDialog.cancelButton.text=Cancel @@ -22,6 +22,7 @@ CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= + CTMalwareScannerOptionsPanel_licenseAddDialog_desc=License Number: CTMalwareScannerOptionsPanel_licenseAddDialog_title=Add a License... CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc=The license number has already been entered diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java index 8af5795517..4a32bc2c40 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java @@ -24,8 +24,9 @@ import org.openide.util.NbBundle.Messages; */ public class CTLicenseDialog extends javax.swing.JDialog { - private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[0-9a-zA-Z]{8}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{12}\\s*$"); + //private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[0-9a-zA-Z]{8}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{12}\\s*$"); + private static final Pattern LICENSE_PATTERN = Pattern.compile("\\s*[a-zA-Z0-9\\-]+?\\s*"); private String licenseString = null; /** diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java index 8f9d61a79e..a77b537e24 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java @@ -49,6 +49,7 @@ public class CTLicensePersistence { if (licenseResponse != null) { File licenseFile = getCTLicenseFile(); try { + licenseFile.getParentFile().mkdirs(); objectMapper.writeValue(licenseFile, licenseResponse); return true; } catch (IOException ex) { @@ -62,7 +63,7 @@ public class CTLicensePersistence { public synchronized Optional loadLicenseResponse() { Optional toRet = Optional.empty(); File licenseFile = getCTLicenseFile(); - if (licenseFile.isFile()) { + if (licenseFile.exists() && licenseFile.isFile()) { try { toRet = Optional.ofNullable(objectMapper.readValue(licenseFile, LicenseResponse.class)); } catch (IOException ex) { diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index 6ff5e74edc..97e0f0ccee 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -162,11 +162,14 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.authTokenFetcher.cancel(true); } - setMalwareScansDisplay(null, Bundle.CTOPtionsPanel_loadMalwareScansInfo_loading()); - if (licenseInfo == null || licenseInfo.getDecryptedLicense() == null) { + setMalwareScansDisplay(null, null); return; } + + setMalwareScansDisplay(null, Bundle.CTOPtionsPanel_loadMalwareScansInfo_loading()); + + this.authTokenFetcher = new AuthTokenFetcher(licenseInfo.getDecryptedLicense().getBoostLicenseId()); this.authTokenFetcher.execute(); @@ -388,7 +391,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.licenseInfoUserLabel.setVisible(false); } else { this.licenseInfoExpiresLabel.setVisible(true); - this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires(LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); + this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires(this.licenseInfo.getDecryptedLicense().getExpirationDate() == null ? "" : LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); this.licenseInfoIdLabel.setVisible(true); this.licenseInfoIdLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_id(this.licenseInfo.getDecryptedLicense().getBoostLicenseId())); this.licenseInfoUserLabel.setVisible(true); @@ -412,7 +415,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.maxFileUploadsLabel.setVisible(true); this.maxFileUploadsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups(this.authTokenResponse.getFileUploadLimit())); this.countersResetLabel.setVisible(true); - this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getExpiration()))); + this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(this.authTokenResponse.getExpiration() == null ? "" : MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getExpiration()))); this.hashLookupsRemainingLabel.setVisible(true); this.hashLookupsRemainingLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining(remaining(this.authTokenResponse.getHashLookupLimit(), this.authTokenResponse.getHashLookupCount()))); this.fileUploadsRemainingLabel.setVisible(true); diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index b2f08dc42b..ff5e70f4e8 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -64,6 +64,10 @@ com.fasterxml.jackson.databind.util com.fasterxml.jackson.dataformat.csv com.fasterxml.jackson.datatype.jsr310 + com.fasterxml.jackson.datatype.jsr310.deser + com.fasterxml.jackson.datatype.jsr310.deser.key + com.fasterxml.jackson.datatype.jsr310.ser + com.fasterxml.jackson.datatype.jsr310.ser.key com.fasterxml.jackson.datatype.jsr310.util com.github.lgooddatepicker.components com.github.lgooddatepicker.optionalusertools diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index f8cea13909..4973262834 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Thu, 20 Jul 2023 14:02:30 -0400 +#Thu, 20 Jul 2023 19:55:04 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 2387b67597..a6687c35a0 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Thu, 20 Jul 2023 14:02:30 -0400 +#Thu, 20 Jul 2023 19:55:04 -0400 CTL_MainWindow_Title=Autopsy 4.20.0 CTL_MainWindow_Title_No_Project=Autopsy 4.20.0 From b7ab83efdd13c34789157d94ec19e93a93bb489b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Jul 2023 09:19:48 -0400 Subject: [PATCH 04/29] updates for file rep api --- .../cybertriage/autopsy/ctapi/CtApiDAO.java | 16 +- .../autopsy/ctapi/json/AuthTokenRequest.java | 12 ++ .../autopsy/ctapi/json/AuthTokenResponse.java | 13 +- .../ctapi/json/BoostLicenseResponse.java | 2 + .../autopsy/ctapi/json/CTCloudBean.java | 98 +++++++++++ ...Response.java => CTCloudBeanResponse.java} | 12 +- .../json/CTCloudCorrelationResultBean.java | 53 ++++++ .../autopsy/ctapi/json/CTCloudCostBean.java | 41 +++++ .../ctapi/json/CorrelationFrequency.java | 24 +++ .../ctapi/json/CorrelationResultBean.java | 46 +++++ .../ctapi/json/DecryptedLicenseResponse.java | 15 +- .../ctapi/json/FileReputationResult.java | 123 ------------- .../autopsy/ctapi/json/LicenseResponse.java | 2 + .../autopsy/ctapi/json/MalwareResultBean.java | 165 ++++++++++++++++++ .../autopsy/ctapi/json/MetadataLabel.java | 22 ++- .../autopsy/ctapi/util/ObjectMapperUtil.java | 105 +++++++++-- .../ctcloud/CTMalwareScannerOptionsPanel.java | 23 ++- .../autopsy/malwarescan/BatchProcessor.java | 6 +- .../malwarescan/Bundle.properties-MERGED | 4 +- .../malwarescan/MalwareScanIngestModule.java | 24 +-- .../MalwareScanIngestModuleFactory.java | 4 +- 21 files changed, 620 insertions(+), 190 deletions(-) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java rename Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/{FileReputationResponse.java => CTCloudBeanResponse.java} (77%) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java delete mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index 061f8a83e0..dcb0f823a7 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -16,9 +16,10 @@ package com.basistech.df.cybertriage.autopsy.ctapi; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthenticatedRequestData; +import com.basistech.df.cybertriage.autopsy.ctapi.json.CTCloudBean; +import com.basistech.df.cybertriage.autopsy.ctapi.json.CTCloudBeanResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationRequest; -import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResponse; -import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil; @@ -65,16 +66,17 @@ public class CTApiDAO { } - public AuthTokenResponse getAuthToken(String boostLicenseId) throws CTCloudException { + public AuthTokenResponse getAuthToken(DecryptedLicenseResponse decrypted) throws CTCloudException { AuthTokenRequest authTokenRequest = new AuthTokenRequest() .setAutopsyVersion(getAppVersion()) - .setRequestFileUpload(true) - .setBoostLicenseId(boostLicenseId); + .setRequestFileUpload(false) + .setBoostLicenseId(decrypted.getBoostLicenseId()) + .setHostId(decrypted.getLicenseHostId()); return httpClient.doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); } - public List getReputationResults(AuthenticatedRequestData authenticatedRequestData, List md5Hashes) throws CTCloudException { + public List getReputationResults(AuthenticatedRequestData authenticatedRequestData, List md5Hashes) throws CTCloudException { if (CollectionUtils.isEmpty(md5Hashes)) { return Collections.emptyList(); } @@ -85,7 +87,7 @@ public class CTApiDAO { .setToken(authenticatedRequestData.getToken()) .setHashes(md5Hashes); - FileReputationResponse resp = httpClient.doPost(CTCLOUD_SERVER_HASH_PATH, fileRepReq, FileReputationResponse.class); + CTCloudBeanResponse resp = httpClient.doPost(CTCLOUD_SERVER_HASH_PATH, fileRepReq, CTCloudBeanResponse.class); return resp == null || resp.getItems() == null ? Collections.emptyList() : resp.getItems(); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java index 6cda146c84..4e7a9e42dd 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java @@ -29,6 +29,9 @@ public class AuthTokenRequest { @JsonProperty("requestFileUpload") private boolean requestFileUpload; + @JsonProperty("host_id") + private String hostId; + public String getAutopsyVersion() { return autopsyVersion; } @@ -56,4 +59,13 @@ public class AuthTokenRequest { return this; } + public String getHostId() { + return hostId; + } + + public AuthTokenRequest setHostId(String hostId) { + this.hostId = hostId; + return this; + } + } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java index c936d05dbe..ed2a1b0f39 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -13,20 +13,24 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi.json; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochSecsDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import java.time.ZonedDateTime; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.time.Instant; /** * POJO for an auth token response. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class AuthTokenResponse extends AuthenticatedRequestData { private final Long hashLookupCount; private final Long hashLookupLimit; private final Long fileUploadLimit; private final Long fileUploadCount; private final String fileUploadUrl; - private final ZonedDateTime expiration; + private final Instant expiration; @JsonCreator public AuthTokenResponse( @@ -37,7 +41,8 @@ public class AuthTokenResponse extends AuthenticatedRequestData { @JsonProperty("fileUploadLimit") Long fileUploadLimit, @JsonProperty("fileUploadCount") Long fileUploadCount, @JsonProperty("fileUploadUrl") String fileUploadUrl, - @JsonProperty("expiration") ZonedDateTime expiration + @JsonDeserialize(using=InstantEpochSecsDeserializer.class) + @JsonProperty("expiration") Instant expiration ) { this.token = token; this.apiKey = apiKey; @@ -69,7 +74,7 @@ public class AuthTokenResponse extends AuthenticatedRequestData { return fileUploadUrl; } - public ZonedDateTime getExpiration() { + public Instant getExpiration() { return expiration; } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java index ce213aeba4..381f9843b1 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java @@ -14,12 +14,14 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; /** * POJO for a boost license response object that is a part of the license * response. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class BoostLicenseResponse { private final String version; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java new file mode 100644 index 0000000000..25960094ca --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java @@ -0,0 +1,98 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * + * @author rishwanth + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CTCloudBean { + + public static enum Status { + FOUND, + NOT_FOUND, + ERROR, + LIMITS_EXCEEDED, + BEING_SCANNED; + } + + @Nonnull + @JsonProperty("malware") + private MalwareResultBean malwareResult; + + @JsonProperty("correlation") + private CorrelationResultBean correlationResult; + + @Nonnull + @JsonProperty("md5_hash") + private String md5HashValue; + + @Nullable + @JsonProperty("sha1_hash") + private String sha1HashValue; + + public String getMd5HashValue() { + return md5HashValue; + } + + public String getSha1HashValue() { + return sha1HashValue; + } + + public void setMd5HashValue(String md5HashValue) { + this.md5HashValue = md5HashValue; + } + + public void setSha1HashValue(String sha1HashValue) { + this.sha1HashValue = sha1HashValue; + } + + public MalwareResultBean getMalwareResult() { + return malwareResult; + } + + public void setMalwareResult(MalwareResultBean malwareResult) { + this.malwareResult = malwareResult; + } + + public CorrelationResultBean getCorrelationResult() { + return correlationResult; + } + + public void setCorrelationResult(CorrelationResultBean correlationResult) { + this.correlationResult = correlationResult; + } + + @Override + public String toString() { + return "CTCloudBean{" + + "status=" + malwareResult.getStatus() + + ", malwareDescription=" + malwareResult.getMalwareDescription() + + ", score=" + malwareResult.getCTScore() + + ", md5HashValue=" + md5HashValue + + ", sha1HashValue=" + sha1HashValue + + ", firstSeen=" + malwareResult.getFirstAnalyzedDate() + + ", lastSeen=" + malwareResult.getLastAnalyzedDate() + + ", statusDescription=" + malwareResult.getStatusDescription() + + ", metadata=" + malwareResult.getMetadata() + + '}'; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java similarity index 77% rename from Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java rename to Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java index aaff89b1d1..cc98008ea1 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java @@ -14,24 +14,26 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; /** * Container for file reputation result list response. */ -public class FileReputationResponse { +@JsonIgnoreProperties(ignoreUnknown = true) +public class CTCloudBeanResponse { - private final List items; + private final List items; @JsonCreator - public FileReputationResponse( - @JsonProperty("items") List items + public CTCloudBeanResponse( + @JsonProperty("items") List items ) { this.items = items; } - public List getItems() { + public List getItems() { return items; } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java new file mode 100644 index 0000000000..3406f69e58 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java @@ -0,0 +1,53 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.Nonnull; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class CTCloudCorrelationResultBean { + + @JsonProperty("correlation") + private CorrelationResultBean correlationResult; + + @Nonnull + @JsonProperty("signature") + private String signature; + + public CorrelationResultBean getCorrelationResult() { + return correlationResult; + } + + public void setCorrelationResult(CorrelationResultBean correlationResult) { + this.correlationResult = correlationResult; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + @Override + public String toString() { + return "CTCloudCorrelationResultBean{" + + "correlationResult=" + correlationResult + + ", signature=" + signature + + '}'; + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java new file mode 100644 index 0000000000..f1ab859eef --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java @@ -0,0 +1,41 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author rishwanth + */ +class CTCloudCostBean { + + private final String provider; + + private final Integer units; + + public CTCloudCostBean(@JsonProperty("provider") String provider, @JsonProperty("units") Integer units) { + this.provider = provider; + this.units = units; + } + + public String getProvider() { + return provider; + } + + public Integer getUnits() { + return units; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java new file mode 100644 index 0000000000..13c0051c5a --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java @@ -0,0 +1,24 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +/** + * + * @author rishwanth + */ +public enum CorrelationFrequency { + UNIQUE, + RARE, + COMMON; +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java new file mode 100644 index 0000000000..b7355b5203 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java @@ -0,0 +1,46 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author rishwanth + */ +public class CorrelationResultBean { + + @JsonProperty("frequency") + private CorrelationFrequency frequency; + + @JsonProperty("frequency_description") + private String frequencyDescription; + + public CorrelationFrequency getFrequency() { + return frequency; + } + + public String getFrequencyDescription() { + return frequencyDescription; + } + + public void setFrequency(CorrelationFrequency frequency) { + this.frequency = frequency; + } + + public void setFrequencyDescription(String frequencyDescription) { + this.frequencyDescription = frequencyDescription; + } + +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index 79ef4350d5..d39e36686b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -14,8 +14,9 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; -import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.MDYDateDeserializer; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.ZonedDateTimeDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.time.Instant; @@ -24,6 +25,7 @@ import java.time.ZonedDateTime; /** * POJO for after encrypted boost license has been decrypted. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class DecryptedLicenseResponse { private final String boostLicenseId; @@ -39,17 +41,16 @@ public class DecryptedLicenseResponse { public DecryptedLicenseResponse( @JsonProperty("boostLicenseId") String boostLicenseId, @JsonProperty("licenseHostId") String licenseHostId, - @JsonDeserialize(using=MDYDateDeserializer.class) + @JsonDeserialize(using = ZonedDateTimeDeserializer.class) @JsonProperty("expirationDate") ZonedDateTime expirationDate, @JsonProperty("hashLookups") Long hashLookups, @JsonProperty("fileUploads") Long fileUploads, - @JsonDeserialize(using=InstantEpochMillisDeserializer.class) + @JsonDeserialize(using = InstantEpochMillisDeserializer.class) @JsonProperty("activationTime") Instant activationTime, @JsonProperty("product") String product, - @JsonProperty("limitType") String limitType, - - @JsonProperty("l4jLicenseId") String l4jlicenseId, - @JsonProperty("ctLicenseId") String ctLicenseId + @JsonProperty("limitType") String limitType + // @JsonProperty("l4jLicenseId") String l4jlicenseId, + // @JsonProperty("ctLicenseId") String ctLicenseId ) { this.boostLicenseId = boostLicenseId; this.licenseHostId = licenseHostId; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java deleted file mode 100644 index 53e604e381..0000000000 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationResult.java +++ /dev/null @@ -1,123 +0,0 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ -package com.basistech.df.cybertriage.autopsy.ctapi.json; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.time.ZonedDateTime; -import java.util.List; - -/** - * A file reputation result regarding malware status. - */ -public class FileReputationResult { - - public static enum Status { - FOUND, - NOT_FOUND, - ERROR, - LIMITS_EXCEEDED, - BEING_SCANNED; - } - - public enum CorrelationFrequency { - UNIQUE, - RARE, - COMMON; - } - - private final String malwareDescription; - private final Status status; - private final CTScore score; - private final String md5Hash; - private final String sha1Hash; - private final ZonedDateTime firstScanDate; - private final ZonedDateTime lastScanDate; - private final List metadata; - private final String statusDescription; - private final CorrelationFrequency frequency; - private final String frequencyDescription; - - @JsonCreator - public FileReputationResult( - @JsonProperty("malwareDescription") String malwareDescription, - @JsonProperty("status") Status status, - @JsonProperty("score") CTScore score, - @JsonProperty("md5Hash") String md5Hash, - @JsonProperty("sha1Hash") String sha1Hash, - @JsonProperty("firstScanDate") ZonedDateTime firstScanDate, - @JsonProperty("lastScanDate") ZonedDateTime lastScanDate, - @JsonProperty("metadata") List metadata, - @JsonProperty("statusDescription") String statusDescription, - @JsonProperty("frequency") CorrelationFrequency frequency, - @JsonProperty("frequencyDescription") String frequencyDescription - ) { - this.malwareDescription = malwareDescription; - this.status = status; - this.score = score; - this.md5Hash = md5Hash; - this.sha1Hash = sha1Hash; - this.firstScanDate = firstScanDate; - this.lastScanDate = lastScanDate; - this.metadata = metadata; - this.statusDescription = statusDescription; - this.frequency = frequency; - this.frequencyDescription = frequencyDescription; - } - - public String getMalwareDescription() { - return malwareDescription; - } - - public Status getStatus() { - return status; - } - - public CTScore getScore() { - return score; - } - - public String getMd5Hash() { - return md5Hash; - } - - public String getSha1Hash() { - return sha1Hash; - } - - public ZonedDateTime getFirstScanDate() { - return firstScanDate; - } - - public ZonedDateTime getLastScanDate() { - return lastScanDate; - } - - public List getMetadata() { - return metadata; - } - - public String getStatusDescription() { - return statusDescription; - } - - public CorrelationFrequency getFrequency() { - return frequency; - } - - public String getFrequencyDescription() { - return frequencyDescription; - } - -} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java index 39184322e2..28511cf087 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java @@ -14,11 +14,13 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; /** * Response POJO for request for license. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class LicenseResponse { private final Boolean success; private final Boolean hostChanged; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java new file mode 100644 index 0000000000..7a11359181 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java @@ -0,0 +1,165 @@ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2014 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ +package com.basistech.df.cybertriage.autopsy.ctapi.json; + +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.ZonedDateTimeDeserializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.time.ZonedDateTime; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * + * @author rishwanth + */ +public class MalwareResultBean { + + public static enum Status { + FOUND, + NOT_FOUND, + ERROR, + LIMITS_EXCEEDED, + BEING_SCANNED; + } + + @Nullable + @JsonProperty("malware_description") + private String malwareDescription; + + @Nonnull + @JsonProperty("status") + private Status status; + + @Nonnull + @JsonProperty("score") + private String score; + + private CTScore ctScore; + + @Nullable + @JsonProperty("first_scan_date") + @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + private ZonedDateTime firstAnalyzedDate; + + @Nullable + @JsonProperty("last_scan_date") + @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + private ZonedDateTime lastAnalyzedDate; + + @JsonProperty("metadata") + private List metadata; + + @Nullable + @JsonProperty("status_description") + private String statusDescription; + + + @Nullable + @JsonProperty + private List cost; + + @JsonIgnore + public CTScore getCTScore() { + return ctScore; + } + + public String getScore() { + return score; + } + + public ZonedDateTime getFirstAnalyzedDate() { + return firstAnalyzedDate; + } + + public ZonedDateTime getLastAnalyzedDate() { + return lastAnalyzedDate; + } + + public List getMetadata() { + if (metadata != null) { + return List.copyOf(metadata); + } else { + return List.of(); // empty list + } + } + + public Status getStatus() { + return status; + } + + public String getStatusDescription() { + return statusDescription; + } + + + public String getMalwareDescription() { + return malwareDescription; + } + + public void setMalwareDescription(String malwareDescription) { + this.malwareDescription = malwareDescription; + } + + public void setStatus(Status status) { + this.status = status; + } + + public void setScore(String score) { + this.score = score; + switch(score) { + case "GOOD_HIGH": + this.ctScore = CTScore.NONE; + break; + case "GOOD_MEDIUM": + this.ctScore = CTScore.LIKELY_NONE; + break; + case "BAD_HIGH": + this.ctScore = CTScore.NOTABLE; + break; + case "BAD_MEDIUM": + this.ctScore = CTScore.LIKELY_NOTABLE; + break; + default: + this.ctScore = CTScore.UNKNOWN; + break; + } + } + + public void setFirstAnalyzedDate(ZonedDateTime firstAnalyzedDate) { + this.firstAnalyzedDate = firstAnalyzedDate; + } + + public void setLastAnalyzedDate(ZonedDateTime lastAnalyzedDate) { + this.lastAnalyzedDate = lastAnalyzedDate; + } + + public void setMetadata(List metadata) { + this.metadata = List.copyOf(metadata); + } + + public void setStatusDescription(String statusDescription) { + this.statusDescription = statusDescription; + } + + public List getCost() { + return List.copyOf(cost); + } + + public void setCost(List cost) { + this.cost = List.copyOf(cost); + } +} diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java index 8077250c8b..43d94a879e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java @@ -1,16 +1,26 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template - */ +/** ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret + ** of, Basis Technology Corp. It is given in confidence by Basis Technology + ** and may only be used as permitted under the license agreement under which + ** it has been distributed, and in no other way. + ** + ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. + ** + ** The technical data and information provided herein are provided with + ** `limited rights', and the computer software provided herein is provided + ** with `restricted rights' as those terms are defined in DAR and ASPR + ** 7-104.9(a). + ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; /** - * - * @author gregd + * Metadata entry. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class MetadataLabel { private final String key; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java index 4a5ecd7925..0c82e5bb68 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -21,11 +21,17 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.DateTimeException; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.Date; +import java.util.function.Function; /** * Creates default ObjectMapper @@ -44,41 +50,110 @@ public class ObjectMapperUtil { public ObjectMapper getDefaultObjectMapper() { ObjectMapper defaultMapper = new ObjectMapper(); -// defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); defaultMapper.registerModule(new JavaTimeModule()); return defaultMapper; } - public static class MDYDateDeserializer extends JsonDeserializer { - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("MMM dd, yyyy"); + + public static class ZonedDateTimeDeserializer extends JsonDeserializer { + @Override public ZonedDateTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { - JsonNode node = jp.getCodec().readTree(jp); - String nodeText = node.asText(); + String date = jp.getText(); try { - return ZonedDateTime.parse(nodeText, FORMATTER); + LocalDateTime ldt = LocalDateTime.parse(date, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + return ZonedDateTime.of(ldt, ZoneOffset.UTC); } catch (DateTimeParseException ex) { return null; } } - } - public static class InstantEpochMillisDeserializer extends JsonDeserializer { +// public static class MDYDateDeserializer extends JsonDeserializer { +// +// private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MMM dd, yyyy"); +// +// @Override +// public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { +// JsonNode node = jp.getCodec().readTree(jp); +// String nodeText = node.asText(); +// try { +// return FORMATTER.parse(nodeText); +// } catch (ParseException ex) { +// return null; +// } +// } +// +// } + + public static class EpochTimeDeserializer extends JsonDeserializer { + + private final Function timeDeserializer; + + public EpochTimeDeserializer(Function timeDeserializer) { + this.timeDeserializer = timeDeserializer; + } @Override - public Instant deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { + public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { JsonNode node = jp.getCodec().readTree(jp); - if (!node.isNumber()) { - return null; + + Long timeVal = null; + if (node.isNumber()) { + timeVal = node.asLong(); } else { + String nodeText = node.asText(); try { - long millis = node.asLong(); - return Instant.ofEpochMilli(millis); - } catch (DateTimeException ex) { - return null; + timeVal = Long.parseLong(nodeText); + } catch (NumberFormatException ex) { + // do nothing if can't parse as number } } + + if (timeVal != null) { + try { + return timeDeserializer.apply(timeVal); + } catch (DateTimeException ex) { + // do nothing if can't parse to epoch + } + } + + return null; + } + } + + public static class InstantEpochMillisDeserializer extends EpochTimeDeserializer { + + public InstantEpochMillisDeserializer() { + super(InstantEpochMillisDeserializer::convert); + } + + private static Instant convert(Long longVal) { + try { + return Instant.ofEpochMilli(longVal); + } catch (DateTimeException ex) { + // do nothing if can't parse to epoch + + return null; + } + } + } + + public static class InstantEpochSecsDeserializer extends EpochTimeDeserializer { + + public InstantEpochSecsDeserializer() { + super(InstantEpochSecsDeserializer::convert); + } + + private static Instant convert(Long longVal) { + try { + return Instant.ofEpochSecond(longVal); + } catch (DateTimeException ex) { + // do nothing if can't parse to epoch + + return null; + } } } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index 97e0f0ccee..a1eb7d9386 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -17,12 +17,15 @@ import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel import com.basistech.df.cybertriage.autopsy.ctapi.CTCloudException; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.logging.Level; @@ -34,6 +37,7 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.core.UserPreferences; /** * Options panel for CyberTriage options for importing a CyberTriage incident @@ -43,8 +47,13 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { private static final Logger logger = Logger.getLogger(CTMalwareScannerOptionsPanel.class.getName()); - private static final SimpleDateFormat LICENSE_EXPIRES_FORMAT = new SimpleDateFormat("MMMM d, YYYY"); - private static final SimpleDateFormat MALWARE_SCANS_RESET_FORMAT = new SimpleDateFormat("MMM d, YYYY' at 'h:mma"); + private static final DateTimeFormatter LICENSE_EXPIRES_FORMAT = DateTimeFormatter + .ofPattern("MMMM d, YYYY") + .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); + + private static final DateTimeFormatter MALWARE_SCANS_RESET_FORMAT = DateTimeFormatter + .ofPattern("MMM d, YYYY' at 'h:mma") + .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance(); @@ -171,7 +180,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { - this.authTokenFetcher = new AuthTokenFetcher(licenseInfo.getDecryptedLicense().getBoostLicenseId()); + this.authTokenFetcher = new AuthTokenFetcher(licenseInfo.getDecryptedLicense()); this.authTokenFetcher.execute(); } @@ -491,15 +500,15 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { "CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_desc=A general error occurred while fetching malware scans information. Please try again later.",}) private class AuthTokenFetcher extends SwingWorker { - private final String boostLicenseId; + private final DecryptedLicenseResponse decryptedLicense; - public AuthTokenFetcher(String boostLicenseId) { - this.boostLicenseId = boostLicenseId; + public AuthTokenFetcher(DecryptedLicenseResponse decryptedLicense) { + this.decryptedLicense = decryptedLicense; } @Override protected AuthTokenResponse doInBackground() throws Exception { - return ctApiDAO.getAuthToken(boostLicenseId); + return ctApiDAO.getAuthToken(decryptedLicense); } @Override diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java index 9adb7410ce..46365ff88e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java @@ -70,7 +70,11 @@ public class BatchProcessor { private synchronized void asyncProcessBatch() throws InterruptedException { if (!batchingQueue.isEmpty()) { // wait for previous processing to finish - lastProcessingFuture.wait(millisTimeout); + synchronized (lastProcessingFuture) { + if (!lastProcessingFuture.isDone()) { + lastProcessingFuture.wait(millisTimeout); + } + } // if 'andThen' doesn't run, clear the processing queue processingQueue.clear(); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED index 9b715cf1fb..67b07a28ae 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED @@ -18,6 +18,6 @@ MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Time # {0} - remainingLookups MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low -MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the CyberTriage cloud API for any possible malicious executables. -MalwareScanIngestModuleFactory_displayName=Malware Scan +MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables. +MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scan MalwareScanIngestModuleFactory_version=1.0.0 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index 65a12f59b7..b966a1c667 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -15,7 +15,7 @@ package com.basistech.df.cybertriage.autopsy.malwarescan; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; -import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationResult; +import com.basistech.df.cybertriage.autopsy.ctapi.json.CTCloudBean; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud.CTLicensePersistence; import java.util.ArrayList; @@ -135,8 +135,7 @@ public class MalwareScanIngestModule implements FileIngestModule { throw new IngestModuleException("No saved license was found"); } - String licenseStr = licenseInfoOpt.get().getDecryptedLicense().getBoostLicenseId(); - AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseStr); + AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseInfoOpt.get().getDecryptedLicense()); // syncronously fetch malware scans info // determine lookups remaining @@ -161,6 +160,7 @@ public class MalwareScanIngestModule implements FileIngestModule { licenseInfo = licenseInfoOpt.get(); startupException = null; noMoreHashLookups = false; + runState = RunState.STARTED_UP; } catch (IngestModuleException ex) { startupException = ex; throw startupException; @@ -235,7 +235,7 @@ public class MalwareScanIngestModule implements FileIngestModule { try { // get an auth token with the license - AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseInfo.getDecryptedLicense().getBoostLicenseId()); + AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseInfo.getDecryptedLicense()); // make sure we are in bounds for the remaining scans long remainingScans = remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount()); @@ -256,14 +256,14 @@ public class MalwareScanIngestModule implements FileIngestModule { } // using auth token, get results - List repResult = ctApiDAO.getReputationResults(authTokenResponse, md5Hashes); + List repResult = ctApiDAO.getReputationResults(authTokenResponse, md5Hashes); if (repResult != null && !repResult.isEmpty()) { SleuthkitCase.CaseDbTransaction trans = null; try { trans = tskCase.beginTransaction(); - for (FileReputationResult result : repResult) { - String sanitizedMd5 = sanitizedMd5(result.getMd5Hash()); + for (CTCloudBean result : repResult) { + String sanitizedMd5 = sanitizedMd5(result.getMd5HashValue()); List objIds = md5ToObjId.remove(sanitizedMd5); if (objIds == null || objIds.isEmpty()) { continue; @@ -309,18 +309,20 @@ public class MalwareScanIngestModule implements FileIngestModule { "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes=YES", "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No=NO" }) - private void createAnalysisResult(Long objId, FileReputationResult fileReputationResult, SleuthkitCase.CaseDbTransaction trans) throws Blackboard.BlackboardException { - if (objId == null || fileReputationResult == null) { + private void createAnalysisResult(Long objId, CTCloudBean cloudBean, SleuthkitCase.CaseDbTransaction trans) throws Blackboard.BlackboardException { + if (objId == null || cloudBean == null || cloudBean.getMalwareResult() == null) { return; } - Score score = fileReputationResult.getScore() == null ? Score.SCORE_UNKNOWN : fileReputationResult.getScore().getTskCore(); + Score score = cloudBean.getMalwareResult().getCTScore() == null + ? Score.SCORE_UNKNOWN + : cloudBean.getMalwareResult().getCTScore().getTskCore(); String conclusion = score.getSignificance() == Score.Significance.NOTABLE || score.getSignificance() == Score.Significance.LIKELY_NOTABLE ? Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes() : Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No(); - String justification = fileReputationResult.getStatusDescription(); + String justification = cloudBean.getMalwareResult().getStatusDescription(); tskCase.getBlackboard().newAnalysisResult( malwareType, diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java index 0a7fffb416..0411985955 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java @@ -26,8 +26,8 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; */ @ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) @Messages({ - "MalwareScanIngestModuleFactory_displayName=Malware Scan", - "MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the CyberTriage cloud API for any possible malicious executables.", + "MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scan", + "MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables.", "MalwareScanIngestModuleFactory_version=1.0.0" }) public class MalwareScanIngestModuleFactory extends IngestModuleFactoryAdapter { From b5b196fd651319b7d4a8b7e90f50e4afec83c0d7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Jul 2023 10:06:34 -0400 Subject: [PATCH 05/29] fix for date --- .../ctapi/json/DecryptedLicenseResponse.java | 4 +- .../autopsy/ctapi/util/ObjectMapperUtil.java | 68 +++++++++++++------ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index d39e36686b..1a084e716e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -14,7 +14,7 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; -import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.ZonedDateTimeDeserializer; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.MDYDateDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -41,7 +41,7 @@ public class DecryptedLicenseResponse { public DecryptedLicenseResponse( @JsonProperty("boostLicenseId") String boostLicenseId, @JsonProperty("licenseHostId") String licenseHostId, - @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + @JsonDeserialize(using = MDYDateDeserializer.class) @JsonProperty("expirationDate") ZonedDateTime expirationDate, @JsonProperty("hashLookups") Long hashLookups, @JsonProperty("fileUploads") Long fileUploads, diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java index 0c82e5bb68..ff424bae01 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -25,12 +25,15 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.DateTimeException; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.util.Date; +import java.util.Locale; import java.util.function.Function; /** @@ -55,14 +58,23 @@ public class ObjectMapperUtil { return defaultMapper; } - - public static class ZonedDateTimeDeserializer extends JsonDeserializer { + public static class UTCBaseZonedDateTimeDeserializer extends JsonDeserializer { + + private final DateTimeFormatter formatter; + + public UTCBaseZonedDateTimeDeserializer(DateTimeFormatter formatter) { + this.formatter = formatter; + } @Override public ZonedDateTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { String date = jp.getText(); + if (date == null) { + return null; + } + try { - LocalDateTime ldt = LocalDateTime.parse(date, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + LocalDateTime ldt = LocalDateTime.parse(date, formatter); return ZonedDateTime.of(ldt, ZoneOffset.UTC); } catch (DateTimeParseException ex) { return null; @@ -70,22 +82,40 @@ public class ObjectMapperUtil { } } -// public static class MDYDateDeserializer extends JsonDeserializer { -// -// private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MMM dd, yyyy"); -// -// @Override -// public Date deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { -// JsonNode node = jp.getCodec().readTree(jp); -// String nodeText = node.asText(); -// try { -// return FORMATTER.parse(nodeText); -// } catch (ParseException ex) { -// return null; -// } -// } -// -// } + public static class ZonedDateTimeDeserializer extends UTCBaseZonedDateTimeDeserializer { + + public ZonedDateTimeDeserializer() { + super(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + } + + public static class MDYDateDeserializer extends JsonDeserializer { + + private final DateTimeFormatter formatter; + + public MDYDateDeserializer() { + this.formatter = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .appendPattern("MMM d, [uuuu][uu]") + .toFormatter(Locale.ENGLISH); + } + + @Override + public ZonedDateTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException { + String date = jp.getText(); + if (date == null) { + return null; + } + + try { + LocalDate ld = LocalDate.parse(date, formatter); + LocalDateTime ldt = ld.atStartOfDay(); + return ZonedDateTime.of(ldt, ZoneOffset.UTC); + } catch (DateTimeParseException ex) { + return null; + } + } + } public static class EpochTimeDeserializer extends JsonDeserializer { From eb7bbfd75cebec891d3258fa9b586aa2306a07b3 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Jul 2023 10:43:56 -0400 Subject: [PATCH 06/29] fix --- .../autopsy/ctapi/CTCloudHttpClient.java | 23 ++++++++++- .../cybertriage/autopsy/ctapi/CtApiDAO.java | 29 ++++++++++---- .../autopsy/ctapi/json/AuthTokenResponse.java | 16 +++++++- .../ctapi/json/AuthenticatedRequestData.java | 25 ++++++++---- .../ctapi/json/FileReputationRequest.java | 25 +----------- .../malwarescan/MalwareScanIngestModule.java | 39 +++++++++++++------ 6 files changed, 103 insertions(+), 54 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java index 93658c2057..818ebd2009 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -30,7 +30,10 @@ import java.security.UnrecoverableKeyException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.logging.Level; import javax.net.ssl.KeyManager; @@ -39,6 +42,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -121,17 +126,33 @@ public class CTCloudHttpClient { null ); } - + public O doPost(String urlPath, Object jsonBody, Class classType) throws CTCloudException { + return doPost(urlPath, Collections.emptyMap(), jsonBody, classType); + } + + public O doPost(String urlPath, Map urlReqParams, Object jsonBody, Class classType) throws CTCloudException { String url = Constants.CT_CLOUD_SERVER + urlPath; try { LOGGER.log(Level.INFO, "initiating http connection to ctcloud server"); try (CloseableHttpClient httpclient = createConnection(getProxySettings(), sslContext)) { URIBuilder builder = new URIBuilder(url); + + if (!MapUtils.isEmpty(urlReqParams)) { + for (Entry e : urlReqParams.entrySet()) { + String key = e.getKey(); + String value = e.getValue(); + if (StringUtils.isNotBlank(key) || StringUtils.isNotBlank(value)) { + builder.addParameter(key, value); + } + } + } + URI postURI = builder.build(); HttpPost postRequest = new HttpPost(postURI); + configureRequestTimeout(postRequest); postRequest.setHeader("Content-type", "application/json"); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index dcb0f823a7..04091bb158 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -24,7 +24,9 @@ import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.sleuthkit.autopsy.coreutils.Version; @@ -38,9 +40,8 @@ public class CTApiDAO { private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token"; private static final String CTCLOUD_SERVER_HASH_PATH = "/_ah/api/reputation/v1/query/file/hash/md5?query_types=CORRELATION,MALWARE"; private static final String AUTOPSY_PRODUCT = "AUTOPSY"; - + private static final CTApiDAO instance = new CTApiDAO(); - private CTApiDAO() { } @@ -52,9 +53,8 @@ public class CTApiDAO { private static String getAppVersion() { return Version.getVersion(); } - - private final CTCloudHttpClient httpClient = CTCloudHttpClient.getInstance(); + private final CTCloudHttpClient httpClient = CTCloudHttpClient.getInstance(); public LicenseResponse getLicenseInfo(String licenseString) throws CTCloudException { LicenseRequest licenseRequest = new LicenseRequest() @@ -76,18 +76,31 @@ public class CTApiDAO { return httpClient.doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class); } + private static Map getAuthParams(AuthenticatedRequestData authenticatedRequestData) { + return new HashMap() { + { + put("api_key", authenticatedRequestData.getApiKey()); + put("token", authenticatedRequestData.getToken()); + put("host_id", authenticatedRequestData.getHostId()); + } + }; + } + public List getReputationResults(AuthenticatedRequestData authenticatedRequestData, List md5Hashes) throws CTCloudException { if (CollectionUtils.isEmpty(md5Hashes)) { return Collections.emptyList(); } FileReputationRequest fileRepReq = new FileReputationRequest() - .setApiKey(authenticatedRequestData.getApiKey()) - .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) - .setToken(authenticatedRequestData.getToken()) .setHashes(md5Hashes); - CTCloudBeanResponse resp = httpClient.doPost(CTCLOUD_SERVER_HASH_PATH, fileRepReq, CTCloudBeanResponse.class); + CTCloudBeanResponse resp = httpClient.doPost( + CTCLOUD_SERVER_HASH_PATH, + getAuthParams(authenticatedRequestData), + fileRepReq, + CTCloudBeanResponse.class + ); + return resp == null || resp.getItems() == null ? Collections.emptyList() : resp.getItems(); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java index ed2a1b0f39..09ee768b2c 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -24,13 +24,16 @@ import java.time.Instant; * POJO for an auth token response. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class AuthTokenResponse extends AuthenticatedRequestData { +public class AuthTokenResponse { + private final Long hashLookupCount; private final Long hashLookupLimit; private final Long fileUploadLimit; private final Long fileUploadCount; private final String fileUploadUrl; private final Instant expiration; + private final String token; + private final String apiKey; @JsonCreator public AuthTokenResponse( @@ -41,7 +44,7 @@ public class AuthTokenResponse extends AuthenticatedRequestData { @JsonProperty("fileUploadLimit") Long fileUploadLimit, @JsonProperty("fileUploadCount") Long fileUploadCount, @JsonProperty("fileUploadUrl") String fileUploadUrl, - @JsonDeserialize(using=InstantEpochSecsDeserializer.class) + @JsonDeserialize(using = InstantEpochSecsDeserializer.class) @JsonProperty("expiration") Instant expiration ) { this.token = token; @@ -77,4 +80,13 @@ public class AuthTokenResponse extends AuthenticatedRequestData { public Instant getExpiration() { return expiration; } + + public String getToken() { + return token; + } + + public String getApiKey() { + return apiKey; + } + } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java index 34e0a61af7..5f97369b9e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java @@ -13,17 +13,24 @@ ************************************************************************** */ package com.basistech.df.cybertriage.autopsy.ctapi.json; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * Data required for an authenticated request. */ -public abstract class AuthenticatedRequestData { +public class AuthenticatedRequestData { - @JsonProperty("token") - protected String token; - @JsonProperty("api_key") - protected String apiKey; + private final String token; + private final String apiKey; + private final String hostId; + + public AuthenticatedRequestData(DecryptedLicenseResponse decrypted, AuthTokenResponse authResp) { + this(authResp.getToken(), authResp.getApiKey(), decrypted.getLicenseHostId()); + } + + public AuthenticatedRequestData(String token, String apiKey, String hostId) { + this.token = token; + this.apiKey = apiKey; + this.hostId = hostId; + } public String getToken() { return token; @@ -33,4 +40,8 @@ public abstract class AuthenticatedRequestData { return apiKey; } + public String getHostId() { + return hostId; + } + } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java index 0d256799f9..e328a88d7b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java @@ -19,14 +19,11 @@ import java.util.List; /** * Request for file reputation results. */ -public class FileReputationRequest extends AuthenticatedRequestData { +public class FileReputationRequest { @JsonProperty("hashes") private List hashes; - @JsonProperty("host_id") - private String hostId; - public List getHashes() { return hashes; } @@ -35,24 +32,4 @@ public class FileReputationRequest extends AuthenticatedRequestData { this.hashes = hashes; return this; } - - public FileReputationRequest setToken(String token) { - this.token = token; - return this; - } - - public FileReputationRequest setApiKey(String apiKey) { - this.apiKey = apiKey; - return this; - } - - public String getHostId() { - return hostId; - } - - public FileReputationRequest setHostId(String hostId) { - this.hostId = hostId; - return this; - } - } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index b966a1c667..9ccfc8d7b2 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -15,6 +15,7 @@ package com.basistech.df.cybertriage.autopsy.malwarescan; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse; +import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthenticatedRequestData; import com.basistech.df.cybertriage.autopsy.ctapi.json.CTCloudBean; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud.CTLicensePersistence; @@ -28,6 +29,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -38,6 +40,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Score; @@ -111,6 +114,7 @@ public class MalwareScanIngestModule implements FileIngestModule { private boolean noMoreHashLookups = false; private IngestModuleException startupException; private long dsId = 0; + private long ingestJobId = 0; @Messages({ "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low", @@ -157,6 +161,7 @@ public class MalwareScanIngestModule implements FileIngestModule { BlackboardArtifact.Category.ANALYSIS_RESULT); fileTypeDetector = new FileTypeDetector(); dsId = context.getDataSource().getId(); + ingestJobId = context.getJobId(); licenseInfo = licenseInfoOpt.get(); startupException = null; noMoreHashLookups = false; @@ -256,9 +261,13 @@ public class MalwareScanIngestModule implements FileIngestModule { } // using auth token, get results - List repResult = ctApiDAO.getReputationResults(authTokenResponse, md5Hashes); + List repResult = ctApiDAO.getReputationResults( + new AuthenticatedRequestData(licenseInfo.getDecryptedLicense(), authTokenResponse), + md5Hashes + ); - if (repResult != null && !repResult.isEmpty()) { + List createdArtifacts = new ArrayList<>(); + if (!CollectionUtils.isEmpty(repResult)) { SleuthkitCase.CaseDbTransaction trans = null; try { trans = tskCase.beginTransaction(); @@ -270,7 +279,10 @@ public class MalwareScanIngestModule implements FileIngestModule { } for (Long objId : objIds) { - createAnalysisResult(objId, result, trans); + AnalysisResult res = createAnalysisResult(objId, result, trans); + if (res != null) { + createdArtifacts.add(res); + } } } @@ -279,10 +291,15 @@ public class MalwareScanIngestModule implements FileIngestModule { } finally { if (trans != null) { trans.rollback(); + createdArtifacts.clear(); trans = null; } } + if (!CollectionUtils.isEmpty(createdArtifacts)) { + tskCase.getBlackboard().postArtifacts(createdArtifacts, Bundle.MalwareScanIngestModuleFactory_displayName(), ingestJobId); + } + // if we only processed part of the batch, after processing, notify that we are out of scans. if (exceededScanLimit) { noMoreHashLookups = true; @@ -309,22 +326,22 @@ public class MalwareScanIngestModule implements FileIngestModule { "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes=YES", "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No=NO" }) - private void createAnalysisResult(Long objId, CTCloudBean cloudBean, SleuthkitCase.CaseDbTransaction trans) throws Blackboard.BlackboardException { + private AnalysisResult createAnalysisResult(Long objId, CTCloudBean cloudBean, SleuthkitCase.CaseDbTransaction trans) throws Blackboard.BlackboardException { if (objId == null || cloudBean == null || cloudBean.getMalwareResult() == null) { - return; + return null; } Score score = cloudBean.getMalwareResult().getCTScore() == null - ? Score.SCORE_UNKNOWN + ? Score.SCORE_UNKNOWN : cloudBean.getMalwareResult().getCTScore().getTskCore(); - + String conclusion = score.getSignificance() == Score.Significance.NOTABLE || score.getSignificance() == Score.Significance.LIKELY_NOTABLE ? Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes() : Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No(); - + String justification = cloudBean.getMalwareResult().getStatusDescription(); - tskCase.getBlackboard().newAnalysisResult( + return tskCase.getBlackboard().newAnalysisResult( malwareType, objId, dsId, @@ -333,7 +350,7 @@ public class MalwareScanIngestModule implements FileIngestModule { MALWARE_CONFIG, justification, Collections.emptyList(), - trans); + trans).getAnalysisResult(); } @Messages({ @@ -356,8 +373,6 @@ public class MalwareScanIngestModule implements FileIngestModule { ex); } finally { // set state to shut down and clear any remaining - malwareType = null; - fileTypeDetector = null; noMoreHashLookups = false; runState = RunState.SHUT_DOWN; startupException = null; From 61bb6785fcc6b672a3b6ff9a85617ca868a47fdf Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Jul 2023 11:02:59 -0400 Subject: [PATCH 07/29] batch processor update --- .../autopsy/malwarescan/BatchProcessor.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java index 46365ff88e..29f7ab52f6 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java @@ -17,10 +17,13 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; /** @@ -52,13 +55,6 @@ public class BatchProcessor { public synchronized void clearCurrentBatch() { batchingQueue.clear(); } - - public synchronized void flush(boolean blockUntilFinished) throws InterruptedException { - asyncProcessBatch(); - if (blockUntilFinished) { - lastProcessingFuture.wait(millisTimeout); - } - } public synchronized void add(T item) throws InterruptedException { batchingQueue.add(item); @@ -67,14 +63,29 @@ public class BatchProcessor { } } + public synchronized void flush(boolean blockUntilFinished) throws InterruptedException { + asyncProcessBatch(); + if (blockUntilFinished) { + waitCurrentFuture(); + } + } + + private synchronized void waitCurrentFuture() throws InterruptedException { + synchronized (lastProcessingFuture) { + if (!lastProcessingFuture.isDone()) { + try { + lastProcessingFuture.get(millisTimeout, TimeUnit.MILLISECONDS); + } catch (ExecutionException | TimeoutException ex) { + // ignore timeout + } + } + } + } + private synchronized void asyncProcessBatch() throws InterruptedException { if (!batchingQueue.isEmpty()) { // wait for previous processing to finish - synchronized (lastProcessingFuture) { - if (!lastProcessingFuture.isDone()) { - lastProcessingFuture.wait(millisTimeout); - } - } + waitCurrentFuture(); // if 'andThen' doesn't run, clear the processing queue processingQueue.clear(); From 01f94522155e5eaaca904adccb0cdb449559a745 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 21 Jul 2023 16:22:33 -0400 Subject: [PATCH 08/29] fix issue where panel wouldn't show --- .../autopsy/ctoptions/CTOptionsPanel.java | 58 +++++++++++-------- .../ctcloud/CTMalwareScannerOptionsPanel.java | 22 +++++-- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java index fd235b580e..31fa00cb75 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java @@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; * Options panel for CyberTriage. */ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { + private static final int MAX_SUBPANEL_WIDTH = 500; private static final Logger logger = Logger.getLogger(CTOptionsPanel.class.getName()); @@ -47,6 +48,16 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { Collection coll = Lookup.getDefault().lookupAll(CTOptionsSubPanel.class); Stream panelStream = coll != null ? coll.stream() : Stream.empty(); this.subPanels = panelStream + .map(panel -> { + try { + // lookup is returning singleton instances which means this panel gets messed up when accessed + // from multiple places because the panel's children are being added to a different CTOptionsPanel + return (CTOptionsSubPanel) panel.getClass().getConstructor().newInstance(); + } catch (Exception ex) { + return null; + } + }) + .filter(item -> item != null) .sorted(Comparator.comparing(p -> p.getClass().getSimpleName().toUpperCase())) .collect(Collectors.toList()); addSubOptionsPanels(this.subPanels); @@ -64,7 +75,7 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { } } }); - + GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = i; @@ -76,29 +87,28 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { contentPane.add(subPanel, gridBagConstraints); } - - GridBagConstraints verticalConstraints = new GridBagConstraints(); - verticalConstraints.gridx = 0; - verticalConstraints.gridy = subPanels.size(); - verticalConstraints.weighty = 1; - verticalConstraints.weightx = 0; - - JPanel verticalSpacer = new JPanel(); - - verticalSpacer.setMinimumSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); - verticalSpacer.setPreferredSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); - verticalSpacer.setMaximumSize(new Dimension(MAX_SUBPANEL_WIDTH, Short.MAX_VALUE)); - contentPane.add(verticalSpacer, verticalConstraints); - - - GridBagConstraints horizontalConstraints = new GridBagConstraints(); - horizontalConstraints.gridx = 1; - horizontalConstraints.gridy = 0; - horizontalConstraints.weighty = 0; - horizontalConstraints.weightx = 1; - - JPanel horizontalSpacer = new JPanel(); - contentPane.add(horizontalSpacer, horizontalConstraints); + + GridBagConstraints verticalConstraints = new GridBagConstraints(); + verticalConstraints.gridx = 0; + verticalConstraints.gridy = subPanels.size(); + verticalConstraints.weighty = 1; + verticalConstraints.weightx = 0; + + JPanel verticalSpacer = new JPanel(); + + verticalSpacer.setMinimumSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); + verticalSpacer.setPreferredSize(new Dimension(MAX_SUBPANEL_WIDTH, 0)); + verticalSpacer.setMaximumSize(new Dimension(MAX_SUBPANEL_WIDTH, Short.MAX_VALUE)); + contentPane.add(verticalSpacer, verticalConstraints); + + GridBagConstraints horizontalConstraints = new GridBagConstraints(); + horizontalConstraints.gridx = 1; + horizontalConstraints.gridy = 0; + horizontalConstraints.weighty = 0; + horizontalConstraints.weightx = 1; + + JPanel horizontalSpacer = new JPanel(); + contentPane.add(horizontalSpacer, horizontalConstraints); } /** diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index a1eb7d9386..f4e0728312 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -50,7 +50,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { private static final DateTimeFormatter LICENSE_EXPIRES_FORMAT = DateTimeFormatter .ofPattern("MMMM d, YYYY") .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); - + private static final DateTimeFormatter MALWARE_SCANS_RESET_FORMAT = DateTimeFormatter .ofPattern("MMM d, YYYY' at 'h:mma") .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); @@ -175,11 +175,9 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { setMalwareScansDisplay(null, null); return; } - + setMalwareScansDisplay(null, Bundle.CTOPtionsPanel_loadMalwareScansInfo_loading()); - - this.authTokenFetcher = new AuthTokenFetcher(licenseInfo.getDecryptedLicense()); this.authTokenFetcher.execute(); } @@ -452,8 +450,20 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { @Override protected LicenseInfo doInBackground() throws Exception { + if (this.isCancelled()) { + return null; + } LicenseResponse licenseResponse = ctApiDAO.getLicenseInfo(licenseText); + + if (this.isCancelled()) { + return null; + } + ctPersistence.saveLicenseResponse(licenseResponse); + + if (this.isCancelled()) { + return null; + } return LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); } @@ -508,6 +518,10 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { @Override protected AuthTokenResponse doInBackground() throws Exception { + if (this.isCancelled()) { + return null; + } + return ctApiDAO.getAuthToken(decryptedLicense); } From 7f610eaa92796e7e8dba5ad7102b1d05cc534149 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Sun, 23 Jul 2023 08:55:07 -0400 Subject: [PATCH 09/29] license updates --- .../autopsy/ctapi/CTCloudException.java | 31 +++++++++++-------- .../autopsy/ctapi/CTCloudHttpClient.java | 31 +++++++++++-------- .../cybertriage/autopsy/ctapi/Constants.java | 31 +++++++++++-------- .../cybertriage/autopsy/ctapi/CtApiDAO.java | 31 +++++++++++-------- .../autopsy/ctapi/ProxySettings.java | 31 +++++++++++-------- .../autopsy/ctapi/json/AuthTokenRequest.java | 31 +++++++++++-------- .../autopsy/ctapi/json/AuthTokenResponse.java | 31 +++++++++++-------- .../ctapi/json/AuthenticatedRequestData.java | 31 +++++++++++-------- .../ctapi/json/BoostLicenseResponse.java | 31 +++++++++++-------- .../autopsy/ctapi/json/CTCloudBean.java | 31 +++++++++++-------- .../ctapi/json/CTCloudBeanResponse.java | 31 +++++++++++-------- .../json/CTCloudCorrelationResultBean.java | 31 +++++++++++-------- .../autopsy/ctapi/json/CTCloudCostBean.java | 31 +++++++++++-------- .../autopsy/ctapi/json/CTScore.java | 31 +++++++++++-------- .../ctapi/json/CorrelationFrequency.java | 31 +++++++++++-------- .../ctapi/json/CorrelationResultBean.java | 31 +++++++++++-------- .../ctapi/json/DecryptedLicenseResponse.java | 31 +++++++++++-------- .../ctapi/json/FileReputationRequest.java | 31 +++++++++++-------- .../autopsy/ctapi/json/LicenseInfo.java | 31 +++++++++++-------- .../autopsy/ctapi/json/LicenseRequest.java | 31 +++++++++++-------- .../autopsy/ctapi/json/LicenseResponse.java | 31 +++++++++++-------- .../autopsy/ctapi/json/MalwareResultBean.java | 31 +++++++++++-------- .../autopsy/ctapi/json/MetadataLabel.java | 31 +++++++++++-------- .../ctapi/util/CTHostIDGenerationUtil.java | 31 +++++++++++-------- .../ctapi/util/LicenseDecryptorUtil.java | 31 +++++++++++-------- .../autopsy/ctapi/util/Md5HashUtil.java | 31 +++++++++++-------- .../autopsy/ctapi/util/ObjectMapperUtil.java | 31 +++++++++++-------- .../autopsy/ctoptions/CTOptionsPanel.java | 31 +++++++++++-------- .../ctoptions/CTOptionsPanelController.java | 31 +++++++++++-------- .../ctoptions/ctcloud/CTLicenseDialog.java | 31 +++++++++++-------- .../ctcloud/CTLicensePersistence.java | 31 +++++++++++-------- .../ctcloud/CTMalwareScannerOptionsPanel.java | 31 +++++++++++-------- .../ctoptions/subpanel/CTOptionsSubPanel.java | 31 +++++++++++-------- .../autopsy/malwarescan/BatchProcessor.java | 31 +++++++++++-------- .../malwarescan/MalwareScanIngestModule.java | 31 +++++++++++-------- .../MalwareScanIngestModuleFactory.java | 31 +++++++++++-------- 36 files changed, 648 insertions(+), 468 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java index 8b0ff55ee5..698afc3297 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java index 818ebd2009..d774ff1661 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java index 29403ce7d4..73fd1e96ab 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2016 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi; import java.net.URI; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index 04091bb158..899af5545b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi; import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenRequest; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java index d341b99e57..2d3f0d667f 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java index 4e7a9e42dd..6a093fee8e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenRequest.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java index 09ee768b2c..d23e14289a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochSecsDeserializer; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java index 5f97369b9e..ebb716871a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthenticatedRequestData.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; /** diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java index 381f9843b1..83970a7074 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/BoostLicenseResponse.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java index 25960094ca..6df2dcbdb8 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBean.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java index cc98008ea1..704ee1d19e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudBeanResponse.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java index 3406f69e58..31de5d0057 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCorrelationResultBean.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java index f1ab859eef..9c3272219b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTCloudCostBean.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java index fbb5665fba..4a9b910548 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CTScore.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2014 - 2016 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.google.common.base.MoreObjects; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java index 13c0051c5a..4fdf1bcdd8 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationFrequency.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; /** diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java index b7355b5203..6715acc20b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/CorrelationResultBean.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2020 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index 1a084e716e..bc10ec7703 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java index e328a88d7b..46dfcb7bd4 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/FileReputationRequest.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java index 8045686133..1ee8112105 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; /** diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java index c87878596f..4abda508f5 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java index 28511cf087..4d68f86e5e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java index 7a11359181..a77ec55ac0 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MalwareResultBean.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2014 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.ZonedDateTimeDeserializer; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java index 43d94a879e..60b6ce34ad 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/MetadataLabel.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.fasterxml.jackson.annotation.JsonCreator; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java index 184e8593f9..415a773f59 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/CTHostIDGenerationUtil.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2021 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.util; import com.license4j.HardwareID; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java index 8276b8a55a..f62b57d795 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/LicenseDecryptorUtil.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.util; import com.basistech.df.cybertriage.autopsy.ctapi.json.BoostLicenseResponse; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java index 4ff7d262b7..49878f3e71 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/Md5HashUtil.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2018 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.util; import com.google.common.base.Charsets; import com.google.common.hash.HashCode; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java index ff424bae01..d59d8f8307 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctapi.util; import com.fasterxml.jackson.core.JsonParser; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java index 31fa00cb75..24d78897e2 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions; import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java index 93dfe4960c..12993e1c39 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanelController.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions; import java.beans.PropertyChangeEvent; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java index 4a32bc2c40..11af572890 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; import java.util.regex.Pattern; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java index a77b537e24..695e3b1e75 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index f4e0728312..019d805635 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java index 67f727bb13..73f1fa20c3 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/subpanel/CTOptionsSubPanel.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.ctoptions.subpanel; import javax.swing.JPanel; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java index 29f7ab52f6..dc62f6a05e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.malwarescan; import java.util.ArrayList; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index 9ccfc8d7b2..32fe5aa102 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.malwarescan; import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java index 0411985955..08b813d74c 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java @@ -1,16 +1,21 @@ -/** ************************************************************************* - ** This data and information is proprietary to, and a valuable trade secret - ** of, Basis Technology Corp. It is given in confidence by Basis Technology - ** and may only be used as permitted under the license agreement under which - ** it has been distributed, and in no other way. - ** - ** Copyright (c) 2023 Basis Technology Corp. All rights reserved. - ** - ** The technical data and information provided herein are provided with - ** `limited rights', and the computer software provided herein is provided - ** with `restricted rights' as those terms are defined in DAR and ASPR - ** 7-104.9(a). - ************************************************************************** */ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ package com.basistech.df.cybertriage.autopsy.malwarescan; import com.basistech.df.cybertriage.autopsy.ctoptions.CTOptionsPanel; From dd2b56eb55596c29cd233ae4026f7b0907656642 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Sun, 23 Jul 2023 09:51:44 -0400 Subject: [PATCH 10/29] panel updates --- .../autopsy/ctoptions/Bundle.properties | 4 + .../ctoptions/Bundle.properties-MERGED | 4 + .../autopsy/ctoptions/CTOptionsPanel.java | 23 +++- .../ctoptions/LicenseDisclaimerPanel.form | 117 ++++++++++++++++ .../ctoptions/LicenseDisclaimerPanel.java | 128 ++++++++++++++++++ 5 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.form create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties index 2d9daa4c7b..68677cb3f7 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties @@ -3,3 +3,7 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template OptionsCategory_Name_CyberTriage=CyberTriage OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage +LicenseDisclaimerPanel.disclaimer.text=The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use. +LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from +LicenseDisclaimerPanel.link.text=https://cybertriage.com/autopsy-checkout +LicenseDisclaimerPanel.border.title=Disclaimer diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED index 2d9daa4c7b..68677cb3f7 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED @@ -3,3 +3,7 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template OptionsCategory_Name_CyberTriage=CyberTriage OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage +LicenseDisclaimerPanel.disclaimer.text=The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use. +LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from +LicenseDisclaimerPanel.link.text=https://cybertriage.com/autopsy-checkout +LicenseDisclaimerPanel.border.title=Disclaimer diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java index 24d78897e2..6eb72dd73e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java @@ -39,7 +39,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; */ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { - private static final int MAX_SUBPANEL_WIDTH = 500; + private static final int MAX_SUBPANEL_WIDTH = 650; private static final Logger logger = Logger.getLogger(CTOptionsPanel.class.getName()); @@ -65,10 +65,21 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { .filter(item -> item != null) .sorted(Comparator.comparing(p -> p.getClass().getSimpleName().toUpperCase())) .collect(Collectors.toList()); - addSubOptionsPanels(this.subPanels); + addSubOptionsPanels(new LicenseDisclaimerPanel(), this.subPanels); } - private void addSubOptionsPanels(List subPanels) { + private void addSubOptionsPanels(JPanel disclaimerPanel, List subPanels) { + GridBagConstraints disclaimerConstraints = new GridBagConstraints(); + disclaimerConstraints.gridx = 0; + disclaimerConstraints.gridy = 0; + disclaimerConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + disclaimerConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + disclaimerConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + disclaimerConstraints.weighty = 0; + disclaimerConstraints.weightx = 0; + + contentPane.add(disclaimerPanel, disclaimerConstraints); + for (int i = 0; i < subPanels.size(); i++) { CTOptionsSubPanel subPanel = subPanels.get(i); @@ -83,10 +94,10 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = i; + gridBagConstraints.gridy = i + 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(i == 0 ? 5 : 0, 5, 5, 5); + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); gridBagConstraints.weighty = 0; gridBagConstraints.weightx = 0; @@ -95,7 +106,7 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { GridBagConstraints verticalConstraints = new GridBagConstraints(); verticalConstraints.gridx = 0; - verticalConstraints.gridy = subPanels.size(); + verticalConstraints.gridy = subPanels.size() + 1; verticalConstraints.weighty = 1; verticalConstraints.weightx = 0; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.form new file mode 100644 index 0000000000..83e3c8440a --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.form @@ -0,0 +1,117 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java new file mode 100644 index 0000000000..ffd3760e6f --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java @@ -0,0 +1,128 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ +package com.basistech.df.cybertriage.autopsy.ctoptions; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Disclaimer for license and place to purchase CT license. + */ +public class LicenseDisclaimerPanel extends javax.swing.JPanel { + private static final Logger LOGGER = Logger.getLogger(LicenseDisclaimerPanel.class.getName()); + + private static final String CHECKOUT_PAGE_URL = "https://cybertriage.com/autopsy-checkout"; + + /** + * Creates new form LicenseDisclaimerPanel + */ + public LicenseDisclaimerPanel() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.JLabel disclaimer = new javax.swing.JLabel(); + javax.swing.JLabel purchaseFromLabel = new javax.swing.JLabel(); + javax.swing.JLabel link = new javax.swing.JLabel(); + javax.swing.JPanel spacer = new javax.swing.JPanel(); + + setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.border.title"))); // NOI18N + setMaximumSize(new java.awt.Dimension(2147483647, 90)); + setMinimumSize(new java.awt.Dimension(562, 90)); + setPreferredSize(new java.awt.Dimension(400, 90)); + setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(disclaimer, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.disclaimer.text")); // NOI18N + disclaimer.setVerticalAlignment(javax.swing.SwingConstants.TOP); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + add(disclaimer, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(purchaseFromLabel, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.purchaseFromLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 3); + add(purchaseFromLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(link, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.link.text")); // NOI18N + link.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); + link.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + linkMouseClicked(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); + add(link, gridBagConstraints); + + javax.swing.GroupLayout spacerLayout = new javax.swing.GroupLayout(spacer); + spacer.setLayout(spacerLayout); + spacerLayout.setHorizontalGroup( + spacerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + spacerLayout.setVerticalGroup( + spacerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.weighty = 1.0; + add(spacer, gridBagConstraints); + }// //GEN-END:initComponents + + private void linkMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_linkMouseClicked + if (Desktop.isDesktopSupported()) { + try { + Desktop.getDesktop().browse(new URI(CHECKOUT_PAGE_URL)); + } catch (IOException | URISyntaxException e) { + /* TODO: error handling */ } + } else { + /* TODO: error handling */ + } + }//GEN-LAST:event_linkMouseClicked + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} From 315b8abbc5e6f1de85d47c83ad37709cd0d8cd6b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Sun, 23 Jul 2023 14:09:01 -0400 Subject: [PATCH 11/29] title update; disabled by default --- .../malwarescan/Bundle.properties-MERGED | 2 +- .../MalwareScanIngestModuleFactory.java | 11 ++++- .../autopsy/ingest/IngestJobSettings.java | 44 +++++++++---------- .../ConfigurationModuleManager.java | 6 ++- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED index 67b07a28ae..2f58a293ea 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED @@ -19,5 +19,5 @@ MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Time MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables. -MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scan +MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scanner MalwareScanIngestModuleFactory_version=1.0.0 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java index 08b813d74c..e138116036 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModuleFactory.java @@ -31,15 +31,22 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; */ @ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) @Messages({ - "MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scan", + "MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scanner", "MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables.", "MalwareScanIngestModuleFactory_version=1.0.0" }) public class MalwareScanIngestModuleFactory extends IngestModuleFactoryAdapter { + /** + * @return The display name for the factory (static method). + */ + public static String getDisplayName() { + return Bundle.MalwareScanIngestModuleFactory_displayName(); + } + @Override public String getModuleDisplayName() { - return Bundle.MalwareScanIngestModuleFactory_displayName(); + return MalwareScanIngestModuleFactory.getDisplayName(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index a89d0ec680..5166856681 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.ingest; +import com.basistech.df.cybertriage.autopsy.malwarescan.MalwareScanIngestModuleFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -33,7 +34,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.openide.util.NbBundle; import org.openide.util.io.NbObjectInputStream; import org.openide.util.io.NbObjectOutputStream; @@ -54,6 +58,11 @@ public final class IngestJobSettings { private static final String LAST_FILE_INGEST_FILTER_PROPERTY = "Last_File_Ingest_Filter"; //NON-NLS private static final String MODULE_SETTINGS_FOLDER_NAME = "IngestSettings"; //NON-NLS + private static final Set DEFAULT_DISABLED_MODULES = Stream.of( + "Plaso", + MalwareScanIngestModuleFactory.getDisplayName() + ).collect(Collectors.toSet()); + private static final String MODULE_SETTINGS_FOLDER = Paths.get( Paths.get(PlatformUtil.getUserConfigDirectory()).relativize(Paths.get(PlatformUtil.getModuleConfigDirectory())).toString(), MODULE_SETTINGS_FOLDER_NAME @@ -361,36 +370,23 @@ public final class IngestJobSettings { loadedModuleNames.add(moduleFactory.getModuleDisplayName()); } - /** - * Hard coding Plaso to be disabled by default. loadedModuleNames is - * passed below as the default list of enabled modules so briefly remove - * Plaso from loaded modules to get the list of enabled and disabled - * modules names. Then put Plaso back into loadedModulesNames to let the - * rest of the code continue as before. - */ - final String plasoModuleName = "Plaso"; - boolean plasoLoaded = loadedModuleNames.contains(plasoModuleName); - if (plasoLoaded) { - loadedModuleNames.remove(plasoModuleName); + + List defaultEnabledAndLoaded = new ArrayList<>(); + List defaultDisabledAndLoaded = new ArrayList<>(); + for (String loadedModule: loadedModuleNames) { + if (DEFAULT_DISABLED_MODULES.contains(loadedModule)) { + defaultDisabledAndLoaded.add(loadedModule); + } else { + defaultEnabledAndLoaded.add(loadedModule); + } } /** * Get the enabled/disabled ingest modules settings for this context. By * default, all loaded modules except Plaso are enabled. */ - HashSet enabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(loadedModuleNames)); - HashSet disabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, plasoModuleName); //NON-NLS - - // If plaso was loaded, but appears in neither the enabled nor the - // disabled list, add it to the disabled list. - if (!enabledModuleNames.contains(plasoModuleName) && !disabledModuleNames.contains(plasoModuleName)) { - disabledModuleNames.add(plasoModuleName); - } - - //Put plaso back into loadedModuleNames - if (plasoLoaded) { - loadedModuleNames.add(plasoModuleName); - } + HashSet enabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(defaultEnabledAndLoaded)); + HashSet disabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, makeCsvList(defaultDisabledAndLoaded)); //NON-NLS /** * Check for missing modules and create warnings if any are found. diff --git a/Core/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java b/Core/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java index 9aabc396b1..dabb034d5f 100644 --- a/Core/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java +++ b/Core/src/org/sleuthkit/autopsy/integrationtesting/ConfigurationModuleManager.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.integrationtesting; +import com.basistech.df.cybertriage.autopsy.malwarescan.MalwareScanIngestModuleFactory; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -46,7 +47,10 @@ public class ConfigurationModuleManager { private static final Logger logger = Logger.getLogger(ConfigurationModuleManager.class.getName()); private static final IngestJobSettings.IngestType DEFAULT_INGEST_FILTER_TYPE = IngestJobSettings.IngestType.ALL_MODULES; - private static final Set DEFAULT_EXCLUDED_MODULES = Stream.of("Plaso").collect(Collectors.toSet()); + private static final Set DEFAULT_EXCLUDED_MODULES = Stream.of( + "Plaso", + MalwareScanIngestModuleFactory.getDisplayName() + ).collect(Collectors.toSet()); private static final ConfigDeserializer configDeserializer = new ConfigDeserializer(); /** From 7cb41b4d8a450c5df784461aef226cae0b484229 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Sun, 23 Jul 2023 14:26:37 -0400 Subject: [PATCH 12/29] disable module instead of running without license --- .../malwarescan/Bundle.properties-MERGED | 4 ++ .../malwarescan/MalwareScanIngestModule.java | 56 ++++++++++--------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED index 2f58a293ea..0c97a98c2e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/Bundle.properties-MERGED @@ -18,6 +18,10 @@ MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Time # {0} - remainingLookups MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low +MalwareScanIngestModule_ShareProcessing_noLicense_desc=No Cyber Triage license could be loaded. Cyber Triage processing will be disabled. +MalwareScanIngestModule_ShareProcessing_noLicense_title=No Cyber Triage License +MalwareScanIngestModule_ShareProcessing_noRemaining_desc=There are no more remaining hash lookups for this license at this time. Cyber Triage processing will be disabled. +MalwareScanIngestModule_ShareProcessing_noRemaining_title=No remaining lookups MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables. MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scanner MalwareScanIngestModuleFactory_version=1.0.0 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index 32fe5aa102..beffd8e594 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -111,13 +111,12 @@ public class MalwareScanIngestModule implements FileIngestModule { private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance(); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); - private FileTypeDetector fileTypeDetector; private RunState runState = null; + private SleuthkitCase tskCase = null; + private FileTypeDetector fileTypeDetector = null; private LicenseInfo licenseInfo = null; private BlackboardArtifact.Type malwareType = null; - private boolean noMoreHashLookups = false; - private IngestModuleException startupException; private long dsId = 0; private long ingestJobId = 0; @@ -125,23 +124,28 @@ public class MalwareScanIngestModule implements FileIngestModule { "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low", "# {0} - remainingLookups", "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining", - "MalwareScanIngestModule_malwareTypeDisplayName=Malware" + "MalwareScanIngestModule_malwareTypeDisplayName=Malware", + "MalwareScanIngestModule_ShareProcessing_noLicense_title=No Cyber Triage License", + "MalwareScanIngestModule_ShareProcessing_noLicense_desc=No Cyber Triage license could be loaded. Cyber Triage processing will be disabled.", + "MalwareScanIngestModule_ShareProcessing_noRemaining_title=No remaining lookups", + "MalwareScanIngestModule_ShareProcessing_noRemaining_desc=There are no more remaining hash lookups for this license at this time. Cyber Triage processing will be disabled." }) synchronized void startUp(IngestJobContext context) throws IngestModuleException { // only run this code once per startup - if (runState == RunState.STARTED_UP) { - if (startupException != null) { - throw startupException; - } else { - return; - } + if (runState == RunState.STARTED_UP || runState == RunState.DISABLED) { + return; } try { // get saved license Optional licenseInfoOpt = ctSettingsPersistence.loadLicenseInfo(); if (licenseInfoOpt.isEmpty() || licenseInfoOpt.get().getDecryptedLicense() == null) { - throw new IngestModuleException("No saved license was found"); + notifyWarning( + Bundle.MalwareScanIngestModule_ShareProcessing_noLicense_title(), + Bundle.MalwareScanIngestModule_ShareProcessing_noLicense_desc(), + null); + runState = RunState.DISABLED; + return; } AuthTokenResponse authTokenResponse = ctApiDAO.getAuthToken(licenseInfoOpt.get().getDecryptedLicense()); @@ -150,7 +154,12 @@ public class MalwareScanIngestModule implements FileIngestModule { // determine lookups remaining long lookupsRemaining = remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount()); if (lookupsRemaining <= 0) { - throw new IngestModuleException("There are no more file hash lookups for this license"); + notifyWarning( + Bundle.MalwareScanIngestModule_ShareProcessing_noRemaining_title(), + Bundle.MalwareScanIngestModule_ShareProcessing_noRemaining_desc(), + null); + runState = RunState.DISABLED; + return; } else if (lookupsRemaining < LOW_LOOKUPS_REMAINING) { notifyWarning( Bundle.MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title(), @@ -168,15 +177,12 @@ public class MalwareScanIngestModule implements FileIngestModule { dsId = context.getDataSource().getId(); ingestJobId = context.getJobId(); licenseInfo = licenseInfoOpt.get(); - startupException = null; - noMoreHashLookups = false; + + // set run state to initialized runState = RunState.STARTED_UP; - } catch (IngestModuleException ex) { - startupException = ex; - throw startupException; } catch (Exception ex) { - startupException = new IngestModuleException("An exception occurred on MalwareScanIngestModule startup", ex); - throw startupException; + runState = RunState.DISABLED; + throw new IngestModuleException("An exception occurred on MalwareScanIngestModule startup", ex); } } @@ -192,7 +198,7 @@ public class MalwareScanIngestModule implements FileIngestModule { }) IngestModule.ProcessResult process(AbstractFile af) { try { - if (af.getKnown() != TskData.FileKnown.KNOWN + if (runState == RunState.STARTED_UP && af.getKnown() != TskData.FileKnown.KNOWN && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.getMIMEType(af)).trim().toLowerCase())) { batchProcessor.add(new FileRecord(af.getId(), af.getMd5Hash())); @@ -219,7 +225,7 @@ public class MalwareScanIngestModule implements FileIngestModule { "MalwareScanIngestModule_SharedProcessing_generalProcessingError_title=Hash Lookup Error", "MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc=An error occurred while processing hash lookup results",}) private void handleBatch(List fileRecords) { - if (fileRecords == null || fileRecords.isEmpty() || noMoreHashLookups) { + if (runState != RunState.STARTED_UP || fileRecords == null || fileRecords.isEmpty()) { return; } @@ -250,7 +256,7 @@ public class MalwareScanIngestModule implements FileIngestModule { // make sure we are in bounds for the remaining scans long remainingScans = remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount()); if (remainingScans <= 0) { - noMoreHashLookups = true; + runState = RunState.DISABLED; notifyWarning( Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(), Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(), @@ -307,7 +313,7 @@ public class MalwareScanIngestModule implements FileIngestModule { // if we only processed part of the batch, after processing, notify that we are out of scans. if (exceededScanLimit) { - noMoreHashLookups = true; + runState = RunState.DISABLED; notifyWarning( Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(), Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(), @@ -378,9 +384,7 @@ public class MalwareScanIngestModule implements FileIngestModule { ex); } finally { // set state to shut down and clear any remaining - noMoreHashLookups = false; runState = RunState.SHUT_DOWN; - startupException = null; batchProcessor.clearCurrentBatch(); } } @@ -391,7 +395,7 @@ public class MalwareScanIngestModule implements FileIngestModule { } private enum RunState { - STARTED_UP, SHUT_DOWN + STARTED_UP, DISABLED, SHUT_DOWN } class FileRecord { From dfed21e0b206fc213238717ac2d81c12a7e412c4 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sun, 23 Jul 2023 20:51:45 -0400 Subject: [PATCH 13/29] Update Exception Add UnsatisfiedLinkError to catch --- Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties | 2 +- .../org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED | 3 ++- Core/src/org/sleuthkit/autopsy/datamodel/Installer.java | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index f663054325..75b612a0b4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -101,7 +101,7 @@ ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.desc=no description Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null\! Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""\! -Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n\nDetails: {0} +Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n Is Autopsy or Cyber Triage already running?\n\nDetails: {0} Installer.tskLibErr.err=Fatal Error\! InterestingHits.interestingItems.text=INTERESTING ITEMS InterestingHits.displayName.text=Interesting Items diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 5dbdff8b89..4d958cf39b 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -315,7 +315,7 @@ ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.desc=no description Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null\! Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""\! -Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n\nDetails: {0} +Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n Is Autopsy or Cyber Triage already running?\n\nDetails: {0} Installer.tskLibErr.err=Fatal Error\! InterestingHits.interestingItems.text=INTERESTING ITEMS InterestingHits.displayName.text=Interesting Items @@ -418,6 +418,7 @@ ScoreContent_createSheet_filterType_displayName=Type ScoreContent_createSheet_name_desc=no description ScoreContent_createSheet_name_displayName=Name ScoreContent_ScoreContentNode_name=Score +ScoreContent_ScoreFileNode_type=File ScoreContent_susFilter_text=Suspicious Items SlackFileNode.getActions.viewInNewWin.text=View in New Window SlackFileNode.getActions.viewFileInDir.text=View File in Directory diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java index 81b19800d9..323424efdf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java @@ -70,8 +70,9 @@ public class Installer extends ModuleInstall { logger.log(Level.CONFIG, "Sleuth Kit Version: {0}", skVersion); //NON-NLS } - } catch (Exception e) { + } catch (Exception | UnsatisfiedLinkError e) { logger.log(Level.SEVERE, "Error calling Sleuth Kit library (test call failed)", e); //NON-NLS + logger.log(Level.SEVERE, "Is Autopsy or Cyber Triage already running?)", e); //NON-NLS // Normal error box log handler won't be loaded yet, so show error here. final Component parentComponent = null; // Use default window frame. From 14546f004736e2e0bdaee49121017aada7afd8ac Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Sun, 23 Jul 2023 21:08:46 -0400 Subject: [PATCH 14/29] EULA beginnings --- .../ctoptions/ctcloud/Bundle.properties | 4 +- .../ctcloud/Bundle.properties-MERGED | 4 +- .../ctcloud/CTMalwareScannerOptionsPanel.java | 57 ++++--- .../autopsy/ctoptions/ctcloud/EULA.htm | 1 + .../autopsy/ctoptions/ctcloud/EULADialog.form | 104 ++++++++++++ .../autopsy/ctoptions/ctcloud/EULADialog.java | 154 ++++++++++++++++++ 6 files changed, 303 insertions(+), 21 deletions(-) create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULA.htm create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties index 9b875b95c9..bd06716288 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties @@ -21,4 +21,6 @@ CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= - +EULADialog.cancelButton.text=Cancel +EULADialog.acceptButton.text=Accept +EULADialog.title=Cyber Triage End User License Agreement diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED index 4819107a24..58a1befb1f 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED @@ -22,7 +22,6 @@ CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text= CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text= CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text= CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text= - CTMalwareScannerOptionsPanel_licenseAddDialog_desc=License Number: CTMalwareScannerOptionsPanel_licenseAddDialog_title=Add a License... CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc=The license number has already been entered @@ -54,3 +53,6 @@ CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_desc=A general error o CTMalwareScannerOptionsPanel_MalwareScansFetcher_localErr_title=General Error CTOPtionsPanel_loadLicenseInfo_loading=Loading... CTOPtionsPanel_loadMalwareScansInfo_loading=Loading... +EULADialog.cancelButton.text=Cancel +EULADialog.acceptButton.text=Accept +EULADialog.title=Cyber Triage End User License Agreement diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index 019d805635..df311dea3b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -26,9 +26,12 @@ import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil; +import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException; +import com.fasterxml.jackson.core.JsonProcessingException; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; -import java.text.SimpleDateFormat; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Optional; @@ -36,6 +39,7 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; @@ -441,11 +445,39 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { return total - used; } + private void acceptEulaAndSave(LicenseResponse licenseResponse) { + LicenseInfo licenseInfo = null; + try { + final EULADialog eulaDialog = new EULADialog(WindowManager.getDefault().getMainWindow(), true); + eulaDialog.setLocationRelativeTo(this); + eulaDialog.setSize(eulaDialog.getPreferredSize()); + eulaDialog.setVisible(true); + + if (eulaDialog.isAcceptPressed()) { + ctPersistence.saveLicenseResponse(licenseResponse); + licenseInfo = LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); + } + } catch (IOException | InvalidLicenseException ex) { + logger.log(Level.WARNING, "An error occurred while fetching data", ex); + JOptionPane.showMessageDialog( + CTMalwareScannerOptionsPanel.this, + Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc(), + Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title(), + JOptionPane.ERROR_MESSAGE); + } finally { + synchronized (this) { + this.licenseFetcher = null; + setLicenseDisplay(licenseInfo, null); + loadMalwareScansInfo(licenseInfo); + } + } + } + @NbBundle.Messages({ "CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error", "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error", "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later.",}) - private class LicenseFetcher extends SwingWorker { + private class LicenseFetcher extends SwingWorker { private final String licenseText; @@ -454,29 +486,18 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { } @Override - protected LicenseInfo doInBackground() throws Exception { + protected LicenseResponse doInBackground() throws Exception { if (this.isCancelled()) { return null; } - LicenseResponse licenseResponse = ctApiDAO.getLicenseInfo(licenseText); - - if (this.isCancelled()) { - return null; - } - - ctPersistence.saveLicenseResponse(licenseResponse); - - if (this.isCancelled()) { - return null; - } - return LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); + return ctApiDAO.getLicenseInfo(licenseText); } @Override protected void done() { - LicenseInfo licenseInfo = null; try { - licenseInfo = get(); + LicenseResponse licenseResponse = get(); + SwingUtilities.invokeLater(() -> acceptEulaAndSave(licenseResponse)); } catch (InterruptedException ex) { // ignore cancellation } catch (ExecutionException ex) { @@ -495,9 +516,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title(), JOptionPane.ERROR_MESSAGE); } - } finally { - synchronized (CTMalwareScannerOptionsPanel.this) { CTMalwareScannerOptionsPanel.this.licenseFetcher = null; if (!this.isCancelled()) { diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULA.htm b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULA.htm new file mode 100644 index 0000000000..5b4989807b --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULA.htm @@ -0,0 +1 @@ +

CYBER TRIAGE

END USER LICENSE AGREEMENT

v. 1.17, 1/2023

BASISTECH LLC, A DELAWARE LIMITED LIABILITY COMPANY HAVING ITS MAIN OFFICE AT 1070 BROADWAY, SOMERVILLE,, MASSACHUSETTS 02144 (“BASIS”) IS WILLING TO LICENSE THE SOFTWARE (DEFINED BELOW) TO YOU, AS CUSTOMER (“YOU”) ONLY IF YOU ACCEPT ALL TERMS AND CONDITIONS CONTAINED IN THIS END USER LICENSE AGREEMENT (“AGREEMENT”).  PLEASE READ THE TERMS AND CONDITIONS CAREFULLY.   BY CLICKING OR CHECKING ANY “I ACCEPT,” “I AGREE” OR OTHER SIMILAR BUTTON/CHECK­BOX, YOU AGREE TO BE BOUND BY THE TERMS AND CONDITIONS STATED IN THIS AGREEMENT.  IF YOU ARE ENTERING INTO THIS AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY TO THESE TERMS AND CONDITIONS, IN WHICH CASE THE TERMS “YOU” OR “YOUR” SHALL REFER TO SUCH ENTITY.  IF YOU DO NOT AGREE TO BE BOUND BY THE TERMS AND CONDITIONS OF THIS AGREEMENT, DO NOT CLICK OR CHECK ANY “I ACCEPT,” “I AGREE” OR OTHER SIMILAR BUTTON/CHECK­BOX.

  1. SOFTWARE LICENSE.  
  1. Software. This Agreement applies to the Basis Cyber Triage software (“Software”) provided to You by Basis with this Agreement, and related user/operating documentation (“Documentation”). The Software includes free features (“Free Features”), as well as features that require payment of license fees (“Paid Features”).   Each copy of the Software shall have a version number and a release date (the “Release Date”).
  2. Trial License.  If You have downloaded a copy of the Software for the first time, and You have not purchased  a paid license for Software (directly or via a reseller),, Basis grants to You a non-exclusive trial license to use the Software and Documentation for a trial period of seven (7) days (the “Trial Period”), commencing on Your download of the Software (the “Start Date”).  During the Trial Period, You will be able and permitted to use all Free Features and Paid Features of the Software, subject to compliance with the terms of this Agreement, provided that the Trial License granted to You will be for use of object code only. Upon submission of Your written request, Basis may (but shall have no obligation to) provide You with a special license key to extend the Trial Period for a mutually agreed period. At the end of the Trial Period (including any agreed extension thereof), Your access to and right to use the Paid Features will automatically terminate, unless You have purchased a license for the Paid Features from Basis.
  3. Free Features License. Following the Trial Period, or if You download a copy of the Software not for the first time and have not purchased a paid license for Software, Basis grants to You a  non-exclusive license to use the Free Features of the Software only (“Free License”), and the Documentation, subject to the terms and conditions of this Agreement, commencing upon termination of the Trial Period and/or Your free download of the Software (the “Free License Start Date”) and terminating on the first anniversary of the applicable Release Date (unless earlier terminated pursuant to the terms of this Agreement), provided that the license granted to You will be for use of object code only. Upon termination of Your Free License, You shall have no right to use the Software unless You have purchased a paid license for the Software, or You have received a new Free License from Basis.
  4. Paid License Grant. If You have purchased a paid license from Basis (directly or via a reseller), Basis grants to You a non-exclusive license to use the Software and Documentation, including the Free Features and the Paid Features (“Paid License”), commencing on delivery of a production license key  for the Paid License (“Paid License Start Date”), subject to the terms and conditions of this Agreement.. 
  5. Paid License Term: The license term for a Paid License, which may be annual or perpetual, shall be determined by the specific Software product that You have purchased, as set forth in an applicable quote and as described on the following webpage:
    https://www.cybertriage.com/product-sku/
      (each of the products and services identified by a separate SKU are referred to herein as “Products”).
  6. Delivery/Acceptance.  All Software provided under this Agreement shall be deemed to have been accepted by You upon Your download of the Software or delivery of the applicable license key, whichever occurs first.  A production license key for a Paid License shall be delivered/offered for download promptly after Basis receives payment of the applicable license fees.  
  7. Copies.  All copies of the Software are subject to the provisions of this Agreement, and all titles trademarks, and copyright and restricted rights notices shall be reproduced in any copies.  Copying of the Documentation is permitted for internal use only.  
  8. Ownership.  You acquire only the right to use the Software; You do not acquire any rights of ownership.  All rights, title, and interest in the Software and feedback from You regarding the Software shall at all times remain the property of Basis (or, as applicable, Basis's third party providers).  Basis reserves all rights not expressly granted herein.
  9. Optional License to Hosted Scanning Service.  The Paid Features include an opt-in module for a hosted scanning service (“Scanning Service”) provided by Reversing Labs International, GMBH (“ReversingLabs”).  The Scanning Service allows You to upload files and hashes and have them scanned for malware, with the results being publically available. You must have a valid Trial License or Paid License to access and use the Scanning Service. YOUR USE OF THE SCANNING SERVICE IS OPTIONAL AND UNLESS OTHERWISE AGREED TO IN WRITING BY BASIS, YOU WILL HAVE AN HOURLY OR DAILY LIMIT OF FILE SCANS AND HASH SCANS AS DESCRIBED BY THE APPLICABLE PRODUCT.  TRIAL LICENSES ARE SUBJECT TO DAILY OR HOURLY LIMITS APPLIED AT BASIS’S DISCRETION.  EXCEPT IN THE CASE OF A TRIAL LICENSE, YOUR SUBSCRIPTION PERIOD FOR THE SCANNING SERVICE IS AS DESCRIBED BY THE APPLICABLE PRODUCT.  IF YOU USE THE SCANNING SERVICE, YOU WILL BE DEEMED AN END-USER OF THE SCANNING SERVICE WHO’S PERMISSION TO USE THE SCANNING SERVICE IS SPONSORED BY BASIS, THROUGH BASIS’S REVERSINGLABS ACCOUNT, AND YOU AGREE TO COMPLY WITH THE TERMS OF THE REVERSINGLABS END-USER SOFTWARE LICENSE AGREEMENT, A COPY OF WHICH IS ATTACHED AS EXHIBIT A TO THIS AGREEMENT (THE “SCANNING SERVICE EULA”), AS SUCH TERMS APPLY TO END-USERS,  IF YOU DO NOT WANT TO UPLOAD YOUR FILES OR HASHES, THEN DO NOT USE THE SCANNING SERVICE.
  10. Information Stored by Basis. Basis may retain and store an anonymized list of the hash values uploaded by each End-User, for purposes of improving the analytics available to users of the Software (e.g., to provide statistics on the frequency with which a particular hash value is uploaded). Such information may be stored on a third-party hosted cloud server, controlled by Basis. The foregoing server may also store the following information: (a) License ID numbers (without associating such numbers with named End-Users), for authentication purposes; and (b) the daily number of hash lookups performed by each License ID, for purposes of enforcing license limits. Basis may also retain and store anonymized analytical results. Such results will include the count and type of items scored as “Suspicious” and “Bad”, for purposes of identifying errors in the Software. By using the Software, You agree to the storage of such information by Basis.
  1. OBLIGATIONS.
  1. User Limit.  Each Paid License is limited to use by a single individual. Any use of the Software provided under this Agreement by more than one user is a material breach of this Agreement. If you have a Trial License or a Free License, your license is limited to use by the individual who downloaded the Software.     
  2. Usage Restrictions.  You shall not: (A) sublicense or redistribute the Software; (B) use the Software in a manner that is contrary to applicable law or in violation of any third party rights of privacy or intellectual property rights; or (C) translate, create a derivative work of, reverse engineer, reverse assemble, dissemble, or decompile the Software or any part thereof or otherwise attempt to discover any source code or modify or adapt the Software in any manner or form unless expressly allowed in the Documentation or under applicable law (solely for the purpose of achieving interoperability).  You are responsible for all use of the Software and You shall cause Your users to be in compliance with this Agreement.        
  1. FEES AND PAYMENT.  If You purchased a Paid License, You shall pay the license fees for the applicable Product as specified by Basis.  All fees are non-refundable.
  2. TERM AND TERMINATION.  
  1. Agreement Term.  This Agreement shall remain in effect for as long as you have either a Free License or a Paid License from Basis (“Agreement Term”), unless earlier terminated in accordance with this Section.  
  2. Termination for Breach.  Basis may terminate this Agreement effective immediately upon written notice to You if You breach any provision of this Agreement or of the Scanning Service EULA, and if such breach is curable, fail to cure within thirty (30) days after receipt of Basis’s written notice thereof.    
  3. Termination of Scanning Service.  If as a result of a change in the relationship between Basis and the vendor of the Scanning Service, Basis terminates, replaces or changes vendors, Basis will use commercially reasonable efforts to (A) allow You to continue using the service until the end of Your paid subscription period (if any) and (B) communicate details and terms regarding the new service, if any, to You at or before your next annual subscription renewal.  
  4. Effect of Termination.  Upon termination of any license granted hereunder, You shall immediately cease all use of the applicable Software and delete all copies and license keys in Your possession or under Your control.  Termination or expiration of this Agreement or any license shall not limit either party from pursuing any other remedies available to it, including injunctive relief, nor shall such termination or expiration relieve Your obligation to pay all fees that accrued prior to such termination or expiration.  The following Sections shall survive any termination or expiration: 1.8, 4.4, 8-9 and 10, 12, 13 and 14; and to assist in interpreting such surviving provisions, the definitions of defined terms used in those provisions shall also survive.
  1. TAXES.  Basis fees do not include any local, state, federal or foreign taxes, levies or duties of any nature, including value-added, sale, use or withholding taxes (“Taxes”).  You are responsible for paying all Taxes, excluding only taxes based on Basis's net income.  If Basis has the legal obligation to pay or collect Taxes for which You are obligated to pay pursuant to the terms of this Section, the appropriate amount shall be invoiced to and paid by You unless You provide Basis with a valid tax exemption certificate authorized by the appropriate taxing authority.

  1. MAINTENANCE AND SUPPORT SERVICES.   If the Product you have purchased includes Support Services, as defined below, You shall be entitled to receive the Support Services for the period indicated in the applicable Product description, subject to full payment of the applicable fees.  “Support Services” shall mean e-mail support during regular business hours, Boston, MA, time, as well as updates, bug-fixes and minor version releases, each as defined by Basis and on a “when-and-if-available” basis (“Updates”). Updates shall be subject to this Agreement, as it may be amended and provided to You at the time of the applicable Update.    
  2. AUDIT.  Basis may audit Your use of the Software on thirty (30) days’ advanced written notice.  You will cooperate with the audit, including by providing access to any books, computers, records, or other information that relate or may relate to use of the Software.  Such audit will not unreasonably interfere with Your business activities.  In the event that an audit reveals unauthorized use of the Software, You will reimburse Basis for the reasonable cost of the audit, in addition to such other rights and remedies as Basis may have.  Basis will not conduct an audit more than once per calendar year.
  3. WARRANTIES.  
  1. Software Warranty, Duration and Remedy.  Basis warrants that it has title to the Software and/or the authority to grant licenses to use the Software.  During a period of thirty (30) days starting from the Paid License Start Date, Basis warrants that the Software will substantially perform as described in the Documentation, provided that the Software: (A) has been properly installed and used at all times in accordance with the Documentation, and (B) has not been modified or added to by persons other than Basis.  In the event of breach of the warranty in this Subsection 8.1, You shall promptly give Basis written notice and upon receipt, Basis will promptly repair the Software or replace it with software of substantially similar functionality, or if the error is irreparable in Basis’s reasonable determination, then Basis shall provide You with a refund of Your paid fees.  THE REMEDIES SET FORTH IN THE PREVIOUS SENTENCE ARE YOUR SOLE AND EXCLUSIVE REMEDIES FOR A BREACH OF WARRANTY.  
  2. Disclaimer.  EXCEPT FOR THE WARRANTIES DESCRIBED IN SECTION 8.1 IN CONNECTION WITH A PAID LICENSE, BASIS MAKES NO REPRESENTATIONS OR WARRANTIES REGARDING THE CONTENT, EFFECTIVENESS, USEFULNESS, RELIABILITY, AVAILABILITY, TIMELINESS, QUALITY, NON-INFRINGEMENT, SUITABILITY, ACCURACY OR COMPLETENESS OF THE SOFTWARE OR THE SCANNING SERVICE OR THEIR OPERATION OR THAT THE SOFTWARE OR SCANNING SERVICE WILL OPERATE IN CONJUNCTION WITH OTHER SYSTEM SOFTWARE WHICH YOU MAY SELECT OR THE RESULTS YOU MAY OBTAIN BY USING THE SOFTWARE OR THE SCANNING SERVICE OR THAT THE SOFTWARE OR SCANNING SERVICE WILL BE UNINTERRUPTED OR ERROR-FREE OR THAT THE SOFTWARE OR SCANNING SERVICE WILL MEET YOUR REQUIREMENTS OR THAT ALL SOFTWARE OR SCANNING SERVICE ERRORS WILL BE CORRECTED OR THAT IT IS COMPLETELY SECURE.  THERE ARE NO OTHER WARRANTIES OR REPRESENTATIONS, GUARANTEES OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, THOSE OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT OF THIRD PARTY RIGHTS.  YOU ASSUME ALL RESPONSIBILITY FOR DETERMINING WHETHER THE SOFTWARE AND THE SCANNING SERIVCE OR THE INFORMATION GENERATED BY THEM IS ACCURATE OR SUFFICIENT FOR YOUR PURPOSES.
  1. LIMITATION OF LIABILITY.
  1. Exclusion of Consequential Damages.  YOU AGREE THAT THE CONSIDERATION WHICH BASIS IS CHARGING HEREUNDER DOES NOT INCLUDE CONSIDERATION FOR ASSUMPTION BY BASIS OF THE RISK OF YOUR INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES.  TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BASIS AND ITS LICENSORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, EXEMPLARY, PUNITIVE, SPECIAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF PROFITS, BUSINESS OPPORTUNITIES, REVENUE, DATA, OR USE, INCURRED BY YOU OR ANY THIRD PARTY, WHETHER IN AN ACTION IN CONTRACT OR TORT OR OTHERWISE, ARISING FROM OR RELATED TO THE USE OF THE SOFTWARE OR THE SCANNING SERVICE OR ANY DATA DERIVED THEREFROM, EVEN IF BASIS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  2. Limitation on Direct Damages.  TO THE MAXIMUM EXTENT PERMITTED BY LAW, LIABILITY OF BASIS AND ITS LICENSORS UNDER THIS AGREEMENT FOR DIRECT DAMAGES TO YOU SHALL IN NO EVENT EXCEED THE AMOUNT OF FEES PAID BY YOU UNDER THIS AGREEMENT.
  3. THE FOREGOING LIMITATIONS SHALL APPLY REGARDLESS OF WHETHER BASIS OR ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND REGARDLESS OF WHETHER ANY REMEDY FAILS OF ITS ESSENTIAL PURPOSE.  
  1. COMPLIANCE WITH LAW. You shall comply with all laws applicable to the actions contemplated by this Agreement.  You acknowledge that the Software is of United States origin, is provided subject to the U.S. Export Administration Regulations, may be subject to the export control laws of the applicable territory, and that diversion contrary to applicable export control laws is prohibited. You represent that (A) You are not, and are not acting on behalf of, (1) any person who is a citizen, national, or resident of, or who is controlled by the government of any country to which the United States has prohibited export transactions; or (2) any person or entity listed on the U.S. Treasury Department list of Specially Designated Nationals and Blocked Persons, or the U.S. Commerce Department Denied Persons List or Entity List; and (B) you will not permit the Software to be used for, any purposes prohibited by law, including, any prohibited development, design, manufacture or production of missiles or nuclear, chemical or biological weapons. The Software and accompanying documentation are deemed to be “commercial computer software” and “commercial computer software documentation”, respectively, pursuant to DFARS Section 227.7202 and FAR Section 12.212(b), as applicable. Any use, modification, reproduction, release, performing, displaying or disclosing of the Software and documentation by or for the U.S. Government shall be governed solely by the terms and conditions of this Agreement.  You assume sole responsibility for any required export approval and/or licenses and all related costs and for the violation of any United States export law or regulation or any other law or regulation of an applicable jurisdiction.  
  2. THIRD PARTY COMPONENTS.  The Software may contain third party owned components, some of which are subject to open source licenses.  Except as permitted by any applicable open source licenses, You shall not use, or permit others to use, such third party owned components apart from the Software.  Your license rights with respect to components subject to open source licenses are defined by the terms of such licenses; nothing in this Agreement is intended to alter, enlarge, or restrict Your rights or obligations under the applicable open source licenses with respect to such open source code.
  3. ASSIGNMENT.  You may assign or transfer all of your rights and interests in this Agreement to another party provided that (A) You give prompt prior written notice to Basis; (B) the proposed transferee is not a direct competitor of Basis (as reasonably determined by Basis); (C) You retain no copies after assignment or transfer; and (D) the transferee agrees to all of the terms of this Agreement.  
  4. GDPR ADDENDUM. If You reside in the European Union, or, if You upload to the Scanning Service files or data samples that contain data relating to real persons that reside in the European Union, then this EULA is subject to the GDPR Addendum that is either attached to this EULA or provided to you separately. In those instances where the GDPR Addendum applies and when there is a conflict between this EULA and the GDPR Addendum, the GDPR Addendum controls.
  5. GENERAL TERMS.  For the avoidance of doubt, the Product descriptions are incorporated into this Agreement by this reference.  This Agreement constitutes the complete agreement between the parties and supersedes all previous agreements or representations, written or oral (including any evaluation agreement), with respect to the Software and services specified herein. ANY CUSTOMER TERMS AND CONDITIONS ATTACHED TO OR REFERENCED IN A CUSTOMER PURCHASE ORDER SHALL BE OF NO FORCE AND EFFECT.

This Agreement may not be modified or amended except in a writing signed by a duly authorized representative of each party.  Wherever possible, each provision of this Agreement shall be interpreted in such manner as to be effective and valid under applicable law, but if any provision of this Agreement shall be prohibited by or invalid under applicable law, such provision shall be ineffective only to the extent of such prohibition or invalidity without invalidating the remainder of such provision or the remaining provisions of this Agreement.  The waiver by either party of any default or breach of this Agreement shall not constitute a waiver of any other or subsequent default or breach.  This Agreement is entered into solely for the benefit of Basis and You, and no third party shall be deemed a beneficiary of this Agreement and no third party shall have the right to make any claim or assert any right under it.  This Agreement and all matters relating to or arising out of this Agreement shall be governed and interpreted by and construed under the laws of the Commonwealth of Massachusetts, without reference to its choice of law provisions and without reference to the United Nations Convention on Contracts for the International Sale of Goods.  The parties agree to submit to the exclusive jurisdiction and venue of state or federal courts located in Boston, Massachusetts.  All notices, including notices of address change, required to be sent hereunder shall be in writing and shall be deemed to have been given when sent by registered mail, overnight mail, or a nationally recognized courier to the addresses listed in the Order Form, or by email to the addresses listed in the Order Form, with receipt of acknowledgement (not automatically generated).  Basis and You agree that any principle of construction or rule of law that provides that agreement be construed against the drafter of the agreement in the event of any inconsistency or ambiguity shall not apply to this Agreement.  Except for any obligation by You to make a payment required under this Agreement, neither party will be liable for any failure or delay in performance due, in whole or in part, to causes beyond its reasonable control, such as natural disasters, wars, strikes and other upheavals.


EXHIBIT 1

REVERSING LABS INTERNATIONAL, GMBH

END-USER SOFTWARE LICENSE AGREEMENT

[Agreement follows on the next page]


END-USER SOFTWARE LICENSE AGREEMENT

PLEASE READ THIS END USER LICENSE AGREEMENT (“EULA”) CAREFULLY. THIS EULA IS A LEGAL AGREEMENT BETWEEN YOU (EITHER AN INDIVIDUAL OR A SINGLE ENTITY) (“LICENSEE” OR “YOU”) AND REVERSING LABS INTERNATIONAL, GMBH, A SWISS LIMITED LIABILITY COMPANY (“REVERSINGLABS”). YOU MUST REVIEW AND EITHER ACCEPT OR REJECT THE TERMS OF THIS EULA BEFORE INSTALLING OR USING THE SOFTWARE. BY CLICKING THE “I ACCEPT” BUTTON, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU ACKNOWLEDGE THAT YOU HAVE READ ALL OF THE TERMS AND CONDITIONS OF THIS EULA, UNDERSTAND THEM, AND AGREE TO BE LEGALLY BOUND BY THEM. THIS AGREEMENT IS ENFORCEABLE AGAINST YOU AND ANY LEGAL ENTITY THAT OBTAINED THE SOFTWARE AND ON WHOSE BEHALF IT IS USED: FOR EXAMPLE, IF APPLICABLE, YOUR EMPLOYER. IF YOU DO NOT AGREE TO THE TERMS OF THIS AGREEMENT, DO NOT USE THE SOFTWARE.

If you have entered into a separate agreement with ReversingLabs permitting you to use the Software, that agreement, rather than this EULA, will govern your use of the Software. If all or some portion of the Software has been licensed by another party for your use (e.g., your employer or an individual or company with which you conduct business), your right to use that Software or Data Service and to obtain any related Services is subject to the terms and conditions of the agreement(s) between ReversingLabs and the other party, whether the other party has agreed to the terms of this EULA or to the terms of a separate, written agreement.

If you or your users reside in the European Union, or, if you provide us files or data samples that contain data relating to real persons that reside in the European Union, then this EULA is subject to the GDPR Addendum that is either attached to this EULA or provided to you separately. In those instances where the GDPR Addendum applies and when there is a conflict between this EULA and the GDPR Addendum, the GDPR Addendum controls.

1. DEFINITIONS. The following capitalized terms used in this EULA have the meanings indicated:

1.1 “Account” means an instance of the Software accessible by a single login to the Software.

1.2 “API” means application programming interfaces that ReversingLabs may make available with the Software or as a separate option from time to time. These interfaces allow your programmers to build integrations with the Software.

1.3 “Computer” means an end user personal computer or personal computing device containing one or more CPUs that is not utilized as a Server.

1.4 “Data” means the detailed information related to and samples of known good files, known bad files and Internet locations stored, processed and otherwise maintained in the ReversingLabs’ threat intelligence database, which information is sometimes licensed provided for use with Software and sometimes provided separately, either on a stand-alone basis or as part of a Data Service.

1.5 “Data Service” means any Internet-based service by which ReversingLabs provides Data to you.

1.6 “Delivery Date” means (i) for cloud-based Software, the date on which ReversingLabs sends or otherwise makes available to you the access code(s) and other instructions, if applicable, for utilizing the Software; and (ii) for installed Software, the date on which ReversingLabs sends or makes available to you a digital file containing the Software and any instructions for installation.

1.7 “Documentation” means any online help text and/or manuals, readme files, build notes, functional specifications, instructions, and any related materials provided with the Software. The term "Documentation" includes any updates or modifications ReversingLabs may choose to make available with respect to the Documentation.

1.8 “End User” means you and any human being whose permitted use of any Software or Data Service you sponsor.

1.9 “Intellectual Property Rights" means any rights existing under patent law, copyright law, data and database protection law, trade secret law, and any and all similar proprietary rights of ReversingLabs embodied in the Software.

1.10 “Order Form(s)” means the proposal form provided by ReversingLabs and accepted by you, evidencing your initial purchase of a license and/or subscription to the Software, Data Service and/or Data, plus any subsequent Order Forms submitted online or in written form, in each case specifying, among other things, the specific ReversingLabs Products and Services to be provided to you, the applicable fees, and the term of the agreement. Each Order Form is incorporated into, and therefore is a part of, this Agreement.

1.11 “ReversingLabs Products and Services” means the Software, Data Service, and/or Data licensed to you, as provided in the Order Form associated with this Agreement.

1.12 “Server” means a computer server owned, leased or otherwise controlled by you or a third party on which a licensed copy of a ReversingLabs’ server-based Software product is installed.

1.13 “Services” means hosting services, software maintenance services, support services (including deployment support services), and any other services ReversingLabs may provide you in connection with your use of the Software.

1.14 “Software” means the ReversingLabs software accompanying this EULA, in object code form, together with any of the following that may form a part of it or subsequently be provided by ReversingLabs for use with it: (i) the Data Service; (ii) APIs, tools, toolsets, and other software applications or components; (iii) artwork, photographs, and video or audio content; (iv) Documentation; and (v) any Updates to or Upgrades of any of the foregoing that may be covered by this EULA.

1.15 "Source Code" shall mean computer programs, instructions and related material for implementing proprietary algorithms (and other trade secret or otherwise proprietary methods and processes) written in a human-readable source language in a form capable of serving as the input to a compiler or assembler program, and in form capable of being modified, supported and enhanced by programmers reasonably familiar with the source language. By way of clarification, and not limitation, the term "Source Code" means the preferred form of the code for making modifications to it, including all modules it contains, plus any associated interface definition files, and scripts used to control compilation and installation of an executable or otherwise, create, manage, administer, or operate the hosting environment where the software service is deployed.

1.16 “Updates” means bug fixes, patches, or other additions, revisions to or modifications of Software and/or Data that ReversingLabs provides to you or any End User, including those it makes generally available to customers that subscribe to its maintenance services. An Update to Software typically is identified by a change in a number and/or letter to the right of the first decimal point in a product’s version number. Updates do not include Upgrades.

1.17 “Upgrade” means a major release of Software and/or Data, as determined by ReversingLabs in its sole discretion. An Upgrade to Software and/or Data typically is identified by a new product name or a new number to the left of the first decimal point in the version number of an existing product name.

1.18 “Web Site” means ReversingLabs’ web site located at http://www.reversinglabs.com.

2. OWNERSHIP. The ReversingLabs Products and Services are licensed, not sold. You acknowledge that the ReversingLabs Products and Services (including any changes you may request or suggest) are the property of ReversingLabs and/or its licensors.

Title to the ReversingLabs Products and Services (including each copy of any of them) and all related intellectual property rights embodied in or represented by the ReversingLabs Products and Services will remain with ReversingLabs and/or its licensors at all times. All rights and licenses not expressly granted are reserved to ReversingLabs, and there shall be no licenses or rights implied under this EULA, based on any course of conduct, or otherwise.

3. LICENSE GRANT. Subject to the terms and conditions of this EULA, ReversingLabs, under its Intellectual Property Rights, hereby grants to Licensee:

3.1 For Software and Data: a limited, non-exclusive, non-transferable, worldwide license to access, execute, display, perform, and otherwise use the Software and/or Data, in machine-readable object code, solely for the number of authorized users set forth on your Order Form (if applicable). Licensee shall be entitled to make one (1) copy of any client-side components of the Software (if applicable), solely for archival or backup purposes. To the extent the Order Form includes rights to a Reversing Labs software development kit ("SDK"), ReversingLabs hereby grants you a non-exclusive and non-transferable (except as provided in Section 12.9 (Software, Data and EULA Transfer)) right and license under its Intellectual Property Rights to use and reproduce the SDK on any tangible media in binary code for the sole purpose of integrating the Software with other software and systems via the Software's APIs solely for your internal business use. If the Software you are installing is evaluation or trial use Software, your rights are limited as described below in Section 6.

3.2 License Term. The license granted in Section 3.1 shall be either (i) perpetual or (ii) subscription based for a one year term commencing upon the date the ReversingLabs Products and Services are first made available to you, as set forth in the Order Form.

4. Restrictions. During the term of your license and/or subscription, you agree to comply with the following restrictions and limitations, and you agree not to permit others (including any End User whose use of the ReversingLabs Products and Services you sponsor) to violate them:

4.1 You shall: (i) adopt and enforce such internal policies, procedures and monitoring mechanisms as are reasonably necessary to ensure that the Software is used only in accordance with the terms of this EULA and (ii) take all steps necessary to ensure that no

person or entity will have unauthorized access to the ReversingLabs Products and Services.

4.2 You shall not: (i) copy (except as provided above), sell, rent, assign, sublicense, lease, encumber or otherwise transfer or attempt to transfer the Software, Data, or any portion thereof; (ii) permit any third party to use or have access to the ReversingLabs Products and Services, whether by timesharing, networking (except as expressly permitted hereunder) or any other means; (iii) reverse engineer, decompile, disassemble, or otherwise seek to discover the source code of the ReversingLabs Products and Services; (iv) possess or use the Software or any portion thereof, other than in machine readable object code; (v) make any copies of the Software or Data, other than as permitted by Section 3 hereof; (vi) remove any copyright, trademark, patent or other proprietary notices from the ReversingLabs Products and Services or any portion thereof; (vii) modify, translate, or create any derivative work thereof, but your computer code written to current APIs for the Software that are published by ReversingLabs or otherwise disclosed by ReversingLabs to you will not be considered modifications or derivative works for purposes of this restriction; (viii) modify, disable, circumvent, avoid, bypass, remove, deactivate, impair or otherwise interfere with features of the ReversingLabs Products and Services that enforce license restrictions or limits or report technical or statistical information regarding the ReversingLabs Products and Services or its use to ReversingLabs; or (ix) continue to use prior versions of any ReversingLabs Products and Services after installing an Upgrade of the ReversingLabs Products and Services or any Update or Upgrade that wholly replaces the ReversingLabs Products and Services. To the extent that the right to decompile, disassemble, or reverse engineer the Software is permitted by applicable law, you agree not to do so if ReversingLabs makes available to you a separate software module that allows you to achieve interoperability of an independently created computer program for use with the Software. You agree that, prior to attempting to achieve such interoperability, you will obtain written notification from ReversingLabs that it is unwilling to make such a software module available within a reasonable period of time.

4.3 The ReversingLabs Products and Services activation key(s) and/or user account(s) (if any) is intended solely for your use. You are solely responsible for maintaining the confidentiality and security of your activation key(s) and/or user account(s) (if any). You are solely responsible and liable for any and all use of your activation key(s) and/or user account(s) (if any) and for activities that occur on or through your activation key(s) and/or user account(s) (if any). You agree to notify ReversingLabs immediately about any unauthorized access to, or use of, any of your activation key(s) and/or user account(s) (if any).

4.4 Open Source Material; License Restrictions.

4.4.1 Relevant Definitions. The term "Open Source Software License" means a license under which software is publicly distributed

(or otherwise made publicly available) in source code format, including licenses such as the GNU General Public License (GPL), GNU Lesser General Public License (LGPL), Affero General Public License, Mozilla Public License (MPL), BSD licenses, Artistic License, Apache License and other, similar open source software licenses. The term "Open Source Material" means software that is subject to Open Source Software Licenses.

4.4.2 Use of Open Source Material; Restrictions. ReversingLabs may provide Open Source Material for use in connection with certain Software. You acknowledge that (i) your use of any such Open Source Material is subject to the associated Open Source Software Licenses, and (ii) you shall comply with all such Open Source Software Licenses. A list of applicable Open Source Material and the associated Open Source Software Licenses are available upon request.

5. U.S. Government End Users. The ReversingLabs Products and Services are “commercial item(s)” as defined at 48 C.F.R. 2.101, consisting of "commercial computer software," “computer database,” and "computer software documentation." Notwithstanding anything to the contrary in this EULA, the U.S. Government sometimes makes certain minimum rights of use, reproduction, and disclosure a condition of its purchase or acquisition of commercial software. Accordingly:

5.1 GSA Multiple Award and Federal Supply Schedule Acquisitions. For government purchases or acquisitions through a GSA Multiple Award or Federal Supply Schedule contract, use, reproduction, and disclosure of the Software and Data are subject only to the rights of use, reproduction, and disclosure as stated in Sections 3 and 4 of this EULA. Provided, however, that in the event of a conflict between any provision in Sections 3 and 4 of this EULA and the restrictions set forth in ¶¶ 6 and 9 of GSA’s “Terms and Conditions

Applicable to . . . [SINs] 132-32 . . ., 132-33 . . . 132-34 . . . “ and GSA’s “Terms and Conditions Applicable to . . . [SINs] 132-51 . . . and

132-52,” The foregoing GSA Terms and Conditions shall take precedence. Note, however, that any modification or combination of the ReversingLabs Products and Services under those rights will entirely void the warranty per Section 9.1 of this EULA.

5.2 FAR Acquisitions. For government agency purchases or acquisitions, other than DOD acquisitions subject to 5.3, under the authority of Federal Acquisition Regulation (“FAR”) Part 12, the rights of use, reproduction, and disclosure are only as stated in Sections 3 and 4 of this EULA.

5.3 DOD Acquisitions. For government purchases or acquisitions by the Department of Defense, a Military Department or Defense Agency, the rights of use, reproduction, and disclosure are only as stated in Sections 3 and 4 of this EULA, per DFARS 227.7202-3(a).

6. EVALUATION AND TRIAL USE. If ReversingLabs provides ReversingLabs Products and Services to you for evaluation or trial use (“Evaluation/Trial Use Products and Services”), then your rights are limited as described in this section. You may use the ReversingLabs Evaluation/Trial Use Products and Services in a manner consistent with the terms of this EULA solely for evaluation/trial purposes for a period ranging from two (2) weeks up to sixty (60) days from the Delivery Date, as specified in your Order Form, or for such other period as may be indicated in writing by ReversingLabs at or after the time of delivery. In light of the fact that Evaluation/Trial Use Products and Services are provided to you free of charge, ReversingLabs disclaims the limited warranty set forth below in Section 9.1, and neither ReversingLabs nor any Released Party will be liable for direct damages related to Evaluation/Trial Use Products and Services, as explained more fully in Section 10.2. Access to Evaluation/Trial Use Products and Services may include a "time-out" mechanism that will automatically downgrade or disable the Evaluation/Trial Use Products and Services or otherwise prevent access to the Evaluation/Trial Use Products and Services at the end of the evaluation/trial period.

7. MAINTENANCE AND SUPPORT. Technical support for the ReversingLabs Products and Services may be accessed via email or other contact information provided on the ReversingLabs website (if applicable). Unless you subscribe to an enhanced maintenance and/or support offering, you are not entitled to receive additional maintenance or support for the ReversingLabs Products and Services (though any Updates or Upgrades ReversingLabs may provide you will be covered by this EULA, unless ReversingLabs requires you to accept a new agreement at the time they are provided). If you subscribe to a ReversingLabs maintenance and/or support offering, ReversingLabs will provide you with maintenance and/or support services corresponding to the service level(s) to which you have subscribed, as set forth in a separate agreement you may enter into with ReversingLabs related to such services. Whether or not you subscribe to a maintenance and/or support offering, ReversingLabs reserves the right to provide you with Updates or supplements to the Software when we consider it necessary to do so to ensure that the Software functions properly. Any technical information you provide ReversingLabs in connection with support services it provides you may be used by ReversingLabs for its business purposes, including product and service development.

8. SAMPLE FILE ANALYSIS. You may submit sample files to ReversingLabs for analysis and inclusion in our threat intelligence database ("Sample Files"). We may, in our sole discretion, unpack and analyze Sample Files to expose internal information, including but not limited to IP addresses, names, custom application information, images, compression, encryption, and executable files (the "Analyzed Information"). We may, in our sole discretion, include the Analyzed Information in our database, which is accessible to our other customers. By submitting Sample Files to us, you grant to ReversingLabs under your intellectual property rights a perpetual, irrevocable, worldwide, transferrable, sub-licensable, royalty-free right to use, copy, modify, display, perform, distribute and otherwise exploit the Sample Files. It is your responsibility to use discretion in submitting files to ReversingLabs for analysis. If you believe a file may contain proprietary or other sensitive information that you do not want shared with our other customers, DO NOT SUBMIT SUCH FILE TO REVERSINGLABS. REVERSINGLABS FULLY DISCLAIMS ANY RESPONSIBILITY FOR THE CONTENT OF SAMPLE FILES UPLOADED TO OUR DATABASE. YOU EXPRESSLY AGREE THAT YOUR SUBMISSION OF SAMPLE FILES FOR ANALYSIS AND INCLUSION IN THE REVERSINGLABS DATABASE IS AT YOUR SOLE RISK. If you believe a Sample File has been submitted to us in error (a "Submission Error"), you may contact us and request that the particular file be removed from our database.

REMOVAL OF THE FILE IN QUESTION SHALL BE THE SOLE REMEDY FOR SUBMISSION ERRORS.

9. LIMITED WARRANTIES AND WARRANTY DISCLAIMER.

9.1 ReversingLabs warrants that, for a period of 90 days after the Delivery Date, the Software and/or Data (including any Upgrades for which ReversingLabs does not require you to accept the terms of a replacement agreement, but excluding Updates) will function substantially in accordance with relevant, published specifications (or substantially error free where there are no published specifications). As your exclusive remedy for breach of this warranty, ReversingLabs will, at its option, either replace or repair the defective Software and/or Data or refund all fees paid for it, as well as any fees paid for maintenance, support and Hosted Services associated with the defective Software and/or Data that were and/or are to be provided after the Delivery Date of the defective Software and/or Data. Notwithstanding the foregoing, ReversingLabs will not be responsible for (i) any breach of warranty not reported during

the warranty period; (ii) any malfunctioning of Software and/or Data that you, an End User, or a third party has modified, misused, or damaged; (iii) any malfunctioning of Software and/or Data caused by hardware or network configuration, (iv) any malfunctioning of Software and/or Data caused by third party software or services, or (v) any malfunctioning of Software and/or Data caused by your failure to incorporate all Updates and/or Upgrades provided to you by ReversingLabs. THIS WARRANTY DOES NOT APPLY TO REVERSINGLABS PRODUCTS AND SERVICES COVERED BY SECTION 6 OF THIS EULA.

This warranty gives you specific legal rights. You may also have other legal rights that vary from state to state and country to country.

9.2 LICENSEE SPECIFICALLY ACKNOWLEDGES THAT FILES CONTAINED IN THE REVERSINGLABS DATA SERVICE INCLUDES MALWARE (HARMFUL VIRUSES, TIME BOMBS AND OTHER DISRUPTIVE OR MALICIOUS CODE OR MECHANISMS). LICENSEE EXPRESSLY ASSUMES ALL RISK AND RESPONSIBILITY ASSOCIATED WITH THE POSSESSION, HANDLING AND USE OF MALWARE INCLUDED WITHIN THE DATA SERVICE.

9.3 EXCEPT FOR THE LIMITED WARRANTY SET FORTH IN SECTION 9.1, REVERSINGLABS, ITS LICENSORS AND LICENSORS’ DISTRIBUTORS DISCLAIM ALL WARRANTIES WITH RESPECT TO ALL REVERSINGLABS PRODUCTS AND SERVICES AND ALL THIRD PARTY PRODUCTS OR SERVICES YOU OR END USERS MAY UTILIZE IN CONNECTION WITH REVERSINGLABS PRODUCTS AND SERVICES, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NONINFRINGEMENT. IN PARTICULAR, REVERSINGLABS DOES NOT REPRESENT THAT THE REVERSINGLABS PRODUCTS AND SERVICES ARE ERROR FREE, WILL OPERATE IN AN UNINTERRUPTED MANNER, ARE COMPLETELY SECURE, OR WILL INTEROPERATE WITH THIRD PARTY SOFTWARE OR SERVICES. UNLESS YOU HAVE SUBSCRIBED TO A SERVICES OFFERING THAT GUARANTEES A PARTICULAR LEVEL OF SERVICE AND/OR A FIXED TERM OF SERVICE, ALL SERVICES ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS AND ARE SUBJECT TO CHANGE OR TERMINATION AT ANY TIME AND FOR ANY REASON WITHOUT NOTICE. THE REVERSINGLABS PRODUCTS AND SERVICES ARE NOT DESIGNED OR MANUFACTURED FOR USE IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE SUPPORT SYSTEMS, OR WEAPON OR COMBAT SYSTEMS, IN WHICH THEIR FAILURE COULD LEAD DIRECTLY TO PERSONAL INJURY, DEATH, OR PROPERTY OR ENVIRONMENTAL DAMAGE. REVERSINGLABS DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR SUCH USES. REVERSINGLABS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES WITH RESPECT TO ANY AND ALL MALWARE INCLUDED WITHIN THE DATA.

9.4 U.S. Government Customers and End Users. The ReversingLabs Products and Services are “commercial items” as defined at 48 C.F.R. 2.101, consisting of "commercial computer software," “computer database,” and " computer software documentation." For government purchases or acquisitions through a GSA Supply Schedule contract, the government customer and end user accept the standard, commercial ReversingLabs warranty terms per ¶ 3.a of GSA’s “Terms and Conditions Applicable to . . . [SINs] 132-32 . . ., 132-33 . . . and 132-34 . . .” For government purchases or acquisitions under the authority of Federal Acquisition Regulation (“FAR”) Part 12, the government customer and end user accept the standard, commercial ReversingLabs warranty terms and FAR 52.212-4(p). For all government purchases or acquisitions that are not through a GSA Multiple Award or Federal Supply Schedule contract, the government customer and end user accept the standard, commercial ReversingLabs warranty per FAR 46.709.

10. EXCLUSION OF DAMAGES AND LIMITATION OF LIABILITY.

10.1 TO THE MAXIMUM EXTENT PERMITTED BY LAW (INCLUDING ANY APPLICABLE CONSUMER PROTECTION LAW OF A FOREIGN JURISDICTION), NEITHER REVERSINGLABS NOR ANY OF ITS DIRECTORS, OFFICERS, EMPLOYEES, CONTROLLED OR CONTROLLING ENTITIES, LICENSORS OR LICENSORS’ DISTRIBUTORS (EACH, A “RELEASED PARTY”), WILL HAVE ANY LIABILITY TO YOU OR ANY END USERS FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING, WITHOUT LIMITATION, ANY LOSS OF USE, LOST PROFITS, BUSINESS OR REVENUE, LOSS OF GOODWILL OR OTHER ECONOMIC ADVANTAGE, OR LOSS OF PRIVACY) ARISING OUT OF OR RELATED TO THIS EULA, OR THE REVERSINGLABS PRODUCTS AND SERVICES, EVEN IF REVERSINGLABS OR A RELEASED PARTY HAS BEEN ADVISED OF, OR KNEW OR SHOULD HAVE KNOWN OF, THE POSSIBILITY OF SUCH DAMAGES. FOR SAKE OF CLARITY, REVERSINGLABS EXPRESSLY DISCLAIMS ALL LIABILITY WITH RESPECT TO ANY AND ALL MALWARE INCLUDED WITHIN THE DATA. TO THE EXTENT THIS EXCLUSION OF LIABILITY IS UNENFORCEABLE, DESPITE THE PARTIES' EXPRESS AGREEMENT TO IT AS AN ESSENTIAL ELEMENT OF THIS AGREEMENT, REVERSINGLABS' LIABILITY WITH RESPECT TO SUCH MALWARE WILL BE LIMITED AS PROVIDED IN SECTION 10.3.

10.2 NOTWITHSTANDING PARAGRAPH 10.1 ABOVE OR ANYTHING ELSE TO THE CONTRARY SET FORTH IN THIS EULA, IF

YOUR CLAIMED DAMAGES ARISE FROM OR RELATE TO REVERSINGLABS PRODUCTS AND SERVICES COVERED BY SECTION 6 OF THIS EULA, THEN, TO THE MAXIMUM EXTENT PERMITTED BY LAW (INCLUDING ANY APPLICABLE CONSUMER PROTECTION LAW OF A FOREIGN JURISDICTION), NEITHER REVERSINGLABS NOR ANY RELEASED PARTY WILL HAVE ANY LIABILITY TO YOU OR ANY END USERS FOR DAMAGES OF ANY KIND ARISING OUT OF OR RELATED TO THIS EULA, THE REVERSINGLABS PRODUCTS AND SERVICES, INCLUDING BUT NOT LIMITED TO DIRECT DAMAGES, EVEN IF REVERSINGLABS OR A RELEASED PARTY HAS BEEN ADVISED OF, OR KNEW OR SHOULD HAVE KNOWN OF, THE POSSIBILITY OF SUCH DAMAGES.

10.3 WITHOUT LIMITING THE SCOPE OR EFFECT OF SECTIONS 10.1 OR 10.2 ABOVE, IN NO EVENT WILL REVERSINGLABS’ AND THE RELEASED PARTIES’ TOTAL LIABILITY WITH RESPECT TO ALL CLAIMS ARISING OUT OF OR RELATED TO THIS EULA, THE REVERSINGLABS PRODUCTS AND SERVICES (INCLUDING CLAIMS OF NEGLIGENCE AND STRICT LIABILITY) EXCEED THE LOWER OF (i) THE AGGREGATE DIRECT DAMAGES ACTUALLY INCURRED BY YOU AND YOUR END USERS, OR (ii) US$5OO.

10.4 SOME JURISDICTIONS LIMIT THE EXCLUSION OF DAMAGES OR LIMITATION OF LIABILITY, SO THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY TO YOU. IF ANY PART OF THE EXCLUSIONS OF DAMAGES OR LIMITATIONS OF LIABILITY SET FORTH IN THIS EULA IS UNENFORCEABLE UNDER APPLICABLE LAW, REVERSINGLABS’ AND THE RELEASED PARTIES’ AGGREGATE LIABILITY WILL BE LIMITED TO THE MAXIMUM EXTENT PERMITTED BY LAW, EVEN IF ANY REMEDY FAILS ITS ESSENTIAL PURPOSE.

11. TERM AND TERMINATION. The term of this EULA shall be as set forth in Section 3.2 unless you and ReversingLabs enter into a new agreement that entirely replaces this EULA or unless ReversingLabs terminates this EULA as provided herein. Without prejudice to any other rights, ReversingLabs may terminate this EULA if you fail to comply with its terms and conditions. If ReversingLabs terminates this EULA, (i) you must immediately stop using the ReversingLabs Products and Services and destroy all copies of the Software, Data and all of its component parts (if applicable), and (ii) ReversingLabs will have no further obligation to provide any Services being provided to you or any End Users as of the termination date. Termination of this Agreement shall not affect rights of your external End Users receiving any Software or Data integrated in or otherwise combined with Licensee’s own products/services

prior to the date of termination, provided, however, that ReversingLabs shall have received payment of any fees owing from Licensee therefor. The parties’ respective rights and obligations under Sections 2 (Ownership), 4 (Restrictions), 8 (Limited Warranty and Warranty Disclaimer), 10 (Exclusion of Damages and Limitation of Liability), and Section 12 (General Provisions) will survive the termination of this EULA.

12. GENERAL PROVISIONS.

12.1 Export Restrictions. You agree to comply with all applicable laws and regulations of governmental bodies and agencies related to use of the ReversingLabs Products and Services and your performance under this EULA. In particular, you acknowledge that the Software and Data is of United States origin, is subject to United States export laws and regulations, and may not be exported or re-exported to certain countries or to persons or entities prohibited from receiving U.S. exports (including Denied Parties, Specially Designated Nationals, and entities on the Bureau of Export Administration Entity List or involved with missile technology or nuclear, chemical or biological weapons). The Software and Data also may be subject to the export, import or other laws of other countries.

You represent that you are eligible to receive favorable treatment under current United States export control laws and regulations, and that you will not use or transfer the Software and Data in violation of any U.S. or foreign laws or regulations, or permit others to do so.

12.2 Data Protection. Each party undertakes to comply with its obligations under the relevant EU data protection and privacy legislation including (where applicable) the EU Data Protection Directive (95/46) and equivalent national legislation.

12.3 Waiver. No delay or omission by either party to exercise any right or power arising upon the other party’s nonperformance or breach will impair that right or power or be construed as a waiver of it. Any waiver must be in writing and signed by the waiving party. A waiver on one occasion will not be construed as a waiver of any subsequent event of nonperformance or breach.

12.4 Severability. If any provision of this EULA is declared to be unenforceable for any reason, the remainder of this EULA will continue in full force and effect, and the unenforceable provision will be deemed modified to the extent necessary to comply with the applicable requirements of law, while retaining to the maximum extent permitted by law its intended effect, scope and economic effect.

12.5 Governing Law. The interpretation and performance of this EULA will be governed by the laws of the Commonwealth of Massachusetts, USA, applicable to contracts executed in and performed entirely within Massachusetts, but excluding any choice of law principles that would result in the application of the laws of another jurisdiction. The parties expressly agree that the United Nations Convention on Contracts for the International Sale of Goods will not apply to this EULA.

12.6 Dispute Resolution. Any litigation arising under or related to this EULA will be brought only in the United States District Court for the District of Massachusetts, or, if federal subject matter jurisdiction is lacking, then in the Massachusetts state court for the division and county in which ReversingLabs’ or its successor’s or assign’s principal office in Massachusetts is then located. You hereby submit to the personal jurisdiction of these courts and waive all objections to placing venue exclusively before them. The prevailing party in any litigation arising under or related to this EULA, in addition to any other relief granted to it, will be entitled to recover from the losing party its reasonable attorneys’ fees and costs incurred in connection with the litigation. Notwithstanding the foregoing, ReversingLabs acknowledges that the Contract Disputes Act, its implementing regulations, and its judicial interpretations may take precedence when the U.S. Government is the party accepting this EULA, if required by law; whenever commercial item protections or other exceptions permit the commercially offered disputes resolution clause to apply, however, it applies in full force.

12.7 Returns. If you are a consumer in a European Union member state, you have the rights conferred by this section. In the European Union, you are entitled to cancel your order for the Software and any associated Services within 14 working days from the date on which you downloaded the Software. You are not entitled to cancel your order for Software or Services if you accept this EULA and install the Software. To cancel your order please discontinue the installation process and notify us of your decision in writing or by email within 14 working days of download at the postal or e-mail address indicated below. ReversingLabs will refund the amount you paid for the cancelled Software and Services within 30 days. If you request a refund, you will not be entitled to use the Software or obtain Services unless you place a new order and pay all charges that then apply.

Reversing Labs International, GmbH
C/O Herbert Trachsler
Seefeldstrasse 283
8008 Zurich
Switzerland

12.8 Payment and Taxes. You agree to pay all applicable fees and other charges for Software and Services you acquire. Unless prepaid, all fees and charges are payable in U.S. dollars and are due net thirty (30) days from the date of invoice. ReversingLabs may charge a late fee of 1.5% per month or the maximum rate allowable by law, whichever is greater, on any balance remaining unpaid for more than thirty (30) days, except that interest on payments by U.S. government customers will be calculated according to the Prompt Payment Act and its implementing regulations. Prices are exclusive of all applicable taxes. You agree to pay all taxes (including but not limited to sales, use, excise, and value-added taxes), tariffs, duties, customs fees or similar charges imposed or levied on all Software and Services you acquire, with the exception of taxes on ReversingLabs' net income.

12.9 Software, Data and EULA Transfer. Except with respect to the ReversingLabs Products and Services covered by Section 6, the initial licensee of any ReversingLabs Products and Services utilized by you on your own Computer or Server may make a one-time, permanent transfer of this EULA and the ReversingLabs Products and Services directly to an individual or a single entity. The transfer must include all of the Software (including all component parts and Documentation) and this EULA, and it may not occur by way of consignment or any other indirect transfer. The transferee of the one-time transfer must agree to comply with the terms of this EULA, including the obligation not to further transfer this Software. You may not otherwise transfer the Software or assign any of your rights or obligations under this EULA.

12.10 Entire Agreement. This EULA and the Order Form(s) incorporated herein set forth the entire agreement between you and ReversingLabs with respect to the ReversingLabs Products and Services, and supersedes all prior communications, understandings and agreements, as well as the terms and conditions set forth in or on any purchase order, acknowledgement form, check, or any other document or instrument you may issue to ReversingLabs or transmit in connection with any payment for the ReversingLabs Products and Services.

Copyright ReversingLabs, Inc. 2012. All Rights Reserved. ReversingLabs, TitaniumCore, TiCore, TitaniumCloud, and TiCloud are trademarks of ReversingLabs Corp.

v. 1.17, 1/2023

\ No newline at end of file diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form new file mode 100644 index 0000000000..f226769266 --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form @@ -0,0 +1,104 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java new file mode 100644 index 0000000000..9a3042b8bc --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java @@ -0,0 +1,154 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JDialog.java to edit this template + */ +package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; + +import java.awt.BorderLayout; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.web.WebView; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import org.apache.commons.io.IOUtils; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * + * @author gregd + */ +public class EULADialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EULADialog.class.getName()); + private static final String EULA_RESOURCE = "EULA.htm"; + + private boolean acceptPressed = false; + + /** + * Creates new form EULADialog + */ + public EULADialog(java.awt.Frame parent, boolean modal) throws IOException { + super(parent, modal); + initComponents(); + loadEULA(); + } + + boolean isAcceptPressed() { + return acceptPressed; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + acceptButton = new javax.swing.JButton(); + javax.swing.JButton cancelButton = new javax.swing.JButton(); + javax.swing.JPanel paddingPanel = new javax.swing.JPanel(); + viewablePanel = new javax.swing.JPanel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(org.openide.util.NbBundle.getMessage(EULADialog.class, "EULADialog.title")); // NOI18N + setPreferredSize(new java.awt.Dimension(500, 500)); + setSize(new java.awt.Dimension(550, 550)); + getContentPane().setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(acceptButton, org.openide.util.NbBundle.getMessage(EULADialog.class, "EULADialog.acceptButton.text")); // NOI18N + acceptButton.setEnabled(false); + acceptButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + acceptButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); + getContentPane().add(acceptButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(EULADialog.class, "EULADialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); + getContentPane().add(cancelButton, gridBagConstraints); + + javax.swing.GroupLayout paddingPanelLayout = new javax.swing.GroupLayout(paddingPanel); + paddingPanel.setLayout(paddingPanelLayout); + paddingPanelLayout.setHorizontalGroup( + paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + paddingPanelLayout.setVerticalGroup( + paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + getContentPane().add(paddingPanel, gridBagConstraints); + + viewablePanel.setLayout(new java.awt.BorderLayout()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(viewablePanel, gridBagConstraints); + + pack(); + }// //GEN-END:initComponents + + private void acceptButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_acceptButtonActionPerformed + acceptPressed = true; + dispose(); + }//GEN-LAST:event_acceptButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton acceptButton; + private javax.swing.JPanel viewablePanel; + // End of variables declaration//GEN-END:variables + + private void loadEULA() throws IOException { + InputStream eulaInputStream = EULADialog.class.getResourceAsStream(EULA_RESOURCE); + final String htmlString = IOUtils.toString(eulaInputStream, StandardCharsets.UTF_8); + final JFXPanel fxPanel = new JFXPanel(); + this.viewablePanel.add(fxPanel, BorderLayout.CENTER); + Platform.runLater(() -> { + WebView webView = new WebView(); + webView.getEngine().loadContent(htmlString, "text/html"); + VBox root = new VBox(webView); + Scene scene = new Scene(root, Color.RED); + fxPanel.setScene(scene); + + SwingUtilities.invokeLater(() -> EULADialog.this.acceptButton.setEnabled(true)); + }); + } +} From b16cbe6c1d3ce7fac490395f1fabea3468eba579 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 07:59:27 -0400 Subject: [PATCH 15/29] eula improvements --- .../ctcloud/CTLicensePersistence.java | 7 +- .../ctcloud/CTMalwareScannerOptionsPanel.java | 30 +++-- .../autopsy/ctoptions/ctcloud/EULADialog.form | 84 ++++++++------ .../autopsy/ctoptions/ctcloud/EULADialog.java | 106 +++++++++--------- .../autopsy/ingest/IngestJobSettings.java | 16 ++- 5 files changed, 139 insertions(+), 104 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java index 695e3b1e75..784e15234b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Paths; import java.util.Optional; import java.util.logging.Level; @@ -55,7 +56,11 @@ public class CTLicensePersistence { File licenseFile = getCTLicenseFile(); try { licenseFile.getParentFile().mkdirs(); - objectMapper.writeValue(licenseFile, licenseResponse); + if (licenseResponse != null) { + objectMapper.writeValue(licenseFile, licenseResponse); + } else if (licenseFile.exists()) { + Files.delete(licenseFile.toPath()); + } return true; } catch (IOException ex) { logger.log(Level.WARNING, "There was an error writing CyberTriage license to file: " + licenseFile.getAbsolutePath(), ex); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index df311dea3b..ac6fe958ab 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -27,11 +27,9 @@ import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse; import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil; import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException; -import com.fasterxml.jackson.core.JsonProcessingException; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Optional; @@ -42,6 +40,7 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; +import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; @@ -445,8 +444,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { return total - used; } - private void acceptEulaAndSave(LicenseResponse licenseResponse) { - LicenseInfo licenseInfo = null; + private void acceptEula(LicenseResponse licenseResponse) { try { final EULADialog eulaDialog = new EULADialog(WindowManager.getDefault().getMainWindow(), true); eulaDialog.setLocationRelativeTo(this); @@ -454,8 +452,9 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { eulaDialog.setVisible(true); if (eulaDialog.isAcceptPressed()) { - ctPersistence.saveLicenseResponse(licenseResponse); - licenseInfo = LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); + // only indicate a change to save if we have accepted EULA for a license + this.licenseInfo = LicenseDecryptorUtil.getInstance().createLicenseInfo(licenseResponse); + this.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); } } catch (IOException | InvalidLicenseException ex) { logger.log(Level.WARNING, "An error occurred while fetching data", ex); @@ -465,11 +464,8 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title(), JOptionPane.ERROR_MESSAGE); } finally { - synchronized (this) { - this.licenseFetcher = null; - setLicenseDisplay(licenseInfo, null); - loadMalwareScansInfo(licenseInfo); - } + setLicenseDisplay(this.licenseInfo, null); + loadMalwareScansInfo(this.licenseInfo); } } @@ -497,9 +493,11 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { protected void done() { try { LicenseResponse licenseResponse = get(); - SwingUtilities.invokeLater(() -> acceptEulaAndSave(licenseResponse)); + SwingUtilities.invokeLater(() -> acceptEula(licenseResponse)); } catch (InterruptedException ex) { - // ignore cancellation + // ignore cancellation; just load current license + setLicenseDisplay(licenseInfo, null); + loadMalwareScansInfo(licenseInfo); } catch (ExecutionException ex) { if (ex.getCause() != null && ex.getCause() instanceof CTCloudException cloudEx) { logger.log(Level.WARNING, "An API error occurred while fetching license information", cloudEx); @@ -516,13 +514,11 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title(), JOptionPane.ERROR_MESSAGE); } + setLicenseDisplay(licenseInfo, null); + loadMalwareScansInfo(licenseInfo); } finally { synchronized (CTMalwareScannerOptionsPanel.this) { CTMalwareScannerOptionsPanel.this.licenseFetcher = null; - if (!this.isCancelled()) { - setLicenseDisplay(licenseInfo, null); - loadMalwareScansInfo(licenseInfo); - } } } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form index f226769266..cf95ed8920 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form @@ -6,8 +6,11 @@ + + + - + @@ -32,6 +35,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -67,38 +116,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java index 9a3042b8bc..75d7af3248 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java @@ -10,12 +10,10 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; -import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.web.WebView; -import javax.swing.JTextPane; import javax.swing.SwingUtilities; import org.apache.commons.io.IOUtils; import org.sleuthkit.autopsy.coreutils.Logger; @@ -44,6 +42,25 @@ public class EULADialog extends javax.swing.JDialog { return acceptPressed; } + private void loadEULA() throws IOException { + InputStream eulaInputStream = EULADialog.class.getResourceAsStream(EULA_RESOURCE); + final String htmlString = IOUtils.toString(eulaInputStream, StandardCharsets.UTF_8); + final JFXPanel fxPanel = new JFXPanel(); + this.viewablePanel.add(fxPanel, BorderLayout.CENTER); + Platform.runLater(() -> { + WebView webView = new WebView(); + webView.setMaxSize(Short.MAX_VALUE, Short.MAX_VALUE); + webView.setPrefSize(Short.MAX_VALUE, Short.MAX_VALUE); + webView.setMinSize(100, 100); + webView.getEngine().loadContent(htmlString, "text/html"); + VBox root = new VBox(webView); + Scene scene = new Scene(root, Color.RED); + fxPanel.setScene(scene); + + SwingUtilities.invokeLater(() -> EULADialog.this.acceptButton.setEnabled(true)); + }); + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -54,17 +71,51 @@ public class EULADialog extends javax.swing.JDialog { private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; + viewablePanel = new javax.swing.JPanel(); + javax.swing.JPanel paddingPanel = new javax.swing.JPanel(); acceptButton = new javax.swing.JButton(); javax.swing.JButton cancelButton = new javax.swing.JButton(); - javax.swing.JPanel paddingPanel = new javax.swing.JPanel(); - viewablePanel = new javax.swing.JPanel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle(org.openide.util.NbBundle.getMessage(EULADialog.class, "EULADialog.title")); // NOI18N - setPreferredSize(new java.awt.Dimension(500, 500)); + setMaximumSize(new java.awt.Dimension(32767, 32767)); + setPreferredSize(new java.awt.Dimension(550, 550)); setSize(new java.awt.Dimension(550, 550)); getContentPane().setLayout(new java.awt.GridBagLayout()); + viewablePanel.setMaximumSize(new java.awt.Dimension(32767, 32767)); + viewablePanel.setPreferredSize(null); + viewablePanel.setLayout(new java.awt.BorderLayout()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + getContentPane().add(viewablePanel, gridBagConstraints); + + paddingPanel.setMaximumSize(new java.awt.Dimension(32767, 0)); + + javax.swing.GroupLayout paddingPanelLayout = new javax.swing.GroupLayout(paddingPanel); + paddingPanel.setLayout(paddingPanelLayout); + paddingPanelLayout.setHorizontalGroup( + paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + paddingPanelLayout.setVerticalGroup( + paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + getContentPane().add(paddingPanel, gridBagConstraints); + org.openide.awt.Mnemonics.setLocalizedText(acceptButton, org.openide.util.NbBundle.getMessage(EULADialog.class, "EULADialog.acceptButton.text")); // NOI18N acceptButton.setEnabled(false); acceptButton.addActionListener(new java.awt.event.ActionListener() { @@ -90,35 +141,6 @@ public class EULADialog extends javax.swing.JDialog { gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5); getContentPane().add(cancelButton, gridBagConstraints); - javax.swing.GroupLayout paddingPanelLayout = new javax.swing.GroupLayout(paddingPanel); - paddingPanel.setLayout(paddingPanelLayout); - paddingPanelLayout.setHorizontalGroup( - paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - paddingPanelLayout.setVerticalGroup( - paddingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - getContentPane().add(paddingPanel, gridBagConstraints); - - viewablePanel.setLayout(new java.awt.BorderLayout()); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - getContentPane().add(viewablePanel, gridBagConstraints); - pack(); }// //GEN-END:initComponents @@ -135,20 +157,4 @@ public class EULADialog extends javax.swing.JDialog { private javax.swing.JButton acceptButton; private javax.swing.JPanel viewablePanel; // End of variables declaration//GEN-END:variables - - private void loadEULA() throws IOException { - InputStream eulaInputStream = EULADialog.class.getResourceAsStream(EULA_RESOURCE); - final String htmlString = IOUtils.toString(eulaInputStream, StandardCharsets.UTF_8); - final JFXPanel fxPanel = new JFXPanel(); - this.viewablePanel.add(fxPanel, BorderLayout.CENTER); - Platform.runLater(() -> { - WebView webView = new WebView(); - webView.getEngine().loadContent(htmlString, "text/html"); - VBox root = new VBox(webView); - Scene scene = new Scene(root, Color.RED); - fxPanel.setScene(scene); - - SwingUtilities.invokeLater(() -> EULADialog.this.acceptButton.setEnabled(true)); - }); - } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index 5166856681..9f2214c520 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -371,8 +371,8 @@ public final class IngestJobSettings { } - List defaultEnabledAndLoaded = new ArrayList<>(); - List defaultDisabledAndLoaded = new ArrayList<>(); + Set defaultEnabledAndLoaded = new HashSet<>(); + Set defaultDisabledAndLoaded = new HashSet<>(); for (String loadedModule: loadedModuleNames) { if (DEFAULT_DISABLED_MODULES.contains(loadedModule)) { defaultDisabledAndLoaded.add(loadedModule); @@ -388,6 +388,18 @@ public final class IngestJobSettings { HashSet enabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(defaultEnabledAndLoaded)); HashSet disabledModuleNames = getModulesNames(this.executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, makeCsvList(defaultDisabledAndLoaded)); //NON-NLS + // double check to ensure all loaded modules are present in one of the lists in case more modules (in case settings didn't have a module) + for (String loadedModule : loadedModuleNames) { + // if neither enabled modules or disabled modules contains the loaded module, add it to the default location + if (!enabledModuleNames.contains(loadedModule) && !disabledModuleNames.contains(loadedModule)) { + if (DEFAULT_DISABLED_MODULES.contains(loadedModule)) { + disabledModuleNames.add(loadedModule); + } else { + enabledModuleNames.add(loadedModule); + } + } + } + /** * Check for missing modules and create warnings if any are found. */ From 947b52dbbddfa3e3db7241a35d4a34dcf91034b2 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 10:29:09 -0400 Subject: [PATCH 16/29] fixes for timezone and customer email --- .../cybertriage/autopsy/ctapi/Constants.java | 1 - .../cybertriage/autopsy/ctapi/CtApiDAO.java | 4 ++- .../autopsy/ctapi/json/AuthTokenResponse.java | 10 ++++++- .../ctapi/json/DecryptedLicenseResponse.java | 27 ++++++++++++------- .../autopsy/ctapi/json/LicenseInfo.java | 10 ------- .../autopsy/ctapi/json/LicenseRequest.java | 13 +++++++++ .../autopsy/ctapi/json/LicenseResponse.java | 10 +++---- .../ctcloud/CTMalwareScannerOptionsPanel.java | 15 +++++++---- .../autopsy/core/UserPreferences.java | 25 +++++++++++++++++ 9 files changed, 82 insertions(+), 33 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java index 73fd1e96ab..fa35dd7dbb 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -20,7 +20,6 @@ package com.basistech.df.cybertriage.autopsy.ctapi; import java.net.URI; -// TODO take out anything sensitive or not used final public class Constants { public static final String CYBER_TRIAGE = "CyberTriage"; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java index 899af5545b..25e264e49a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Version; /** @@ -65,7 +66,8 @@ public class CTApiDAO { LicenseRequest licenseRequest = new LicenseRequest() .setBoostLicenseCode(licenseString) .setHostId(CTHostIDGenerationUtil.generateLicenseHostID()) - .setProduct(AUTOPSY_PRODUCT); + .setProduct(AUTOPSY_PRODUCT) + .setTimeZoneId(UserPreferences.getInferredUserTimeZone()); return httpClient.doPost(LICENSE_REQUEST_PATH, licenseRequest, LicenseResponse.class); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java index d23e14289a..d4ac70e16c 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/AuthTokenResponse.java @@ -18,6 +18,7 @@ */ package com.basistech.df.cybertriage.autopsy.ctapi.json; +import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochSecsDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -39,6 +40,7 @@ public class AuthTokenResponse { private final Instant expiration; private final String token; private final String apiKey; + private final Instant resetDate; @JsonCreator public AuthTokenResponse( @@ -50,7 +52,9 @@ public class AuthTokenResponse { @JsonProperty("fileUploadCount") Long fileUploadCount, @JsonProperty("fileUploadUrl") String fileUploadUrl, @JsonDeserialize(using = InstantEpochSecsDeserializer.class) - @JsonProperty("expiration") Instant expiration + @JsonProperty("expiration") Instant expiration, + @JsonDeserialize(using = InstantEpochMillisDeserializer.class) + @JsonProperty("resetDate") Instant resetDate ) { this.token = token; this.apiKey = apiKey; @@ -60,6 +64,7 @@ public class AuthTokenResponse { this.fileUploadCount = fileUploadCount; this.fileUploadUrl = fileUploadUrl; this.expiration = expiration; + this.resetDate = resetDate; } public Long getHashLookupCount() { @@ -94,4 +99,7 @@ public class AuthTokenResponse { return apiKey; } + public Instant getResetDate() { + return resetDate; + } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index bc10ec7703..5be0996a9e 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -19,13 +19,11 @@ package com.basistech.df.cybertriage.autopsy.ctapi.json; import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.InstantEpochMillisDeserializer; -import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil.MDYDateDeserializer; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.time.Instant; -import java.time.ZonedDateTime; /** * POJO for after encrypted boost license has been decrypted. @@ -35,27 +33,29 @@ public class DecryptedLicenseResponse { private final String boostLicenseId; private final String licenseHostId; - private final ZonedDateTime expirationDate; + private final Instant expirationDate; private final Long hashLookups; private final Long fileUploads; private final Instant activationTime; private final String product; private final String limitType; + private final String timezone; + private final String customerEmail; @JsonCreator public DecryptedLicenseResponse( @JsonProperty("boostLicenseId") String boostLicenseId, @JsonProperty("licenseHostId") String licenseHostId, - @JsonDeserialize(using = MDYDateDeserializer.class) - @JsonProperty("expirationDate") ZonedDateTime expirationDate, + @JsonDeserialize(using = InstantEpochMillisDeserializer.class) + @JsonProperty("expirationDate") Instant expirationDate, @JsonProperty("hashLookups") Long hashLookups, @JsonProperty("fileUploads") Long fileUploads, @JsonDeserialize(using = InstantEpochMillisDeserializer.class) @JsonProperty("activationTime") Instant activationTime, @JsonProperty("product") String product, - @JsonProperty("limitType") String limitType - // @JsonProperty("l4jLicenseId") String l4jlicenseId, - // @JsonProperty("ctLicenseId") String ctLicenseId + @JsonProperty("limitType") String limitType, + @JsonProperty("timezone") String timezone, + @JsonProperty("customerEmail") String customerEmail ) { this.boostLicenseId = boostLicenseId; this.licenseHostId = licenseHostId; @@ -65,6 +65,8 @@ public class DecryptedLicenseResponse { this.activationTime = activationTime; this.product = product; this.limitType = limitType; + this.timezone = timezone; + this.customerEmail = customerEmail; } public String getBoostLicenseId() { @@ -95,8 +97,15 @@ public class DecryptedLicenseResponse { return limitType; } - public ZonedDateTime getExpirationDate() { + public Instant getExpirationDate() { return expirationDate; } + public String getTimezone() { + return timezone; + } + + public String getCustomerEmail() { + return customerEmail; + } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java index 1ee8112105..127df9987d 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseInfo.java @@ -37,14 +37,4 @@ public class LicenseInfo { public DecryptedLicenseResponse getDecryptedLicense() { return decryptedLicense; } - - // TODO - public String getUser() { - return "TBD"; - } - - // TODO - public String getEmail() { - return "TBD"; - } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java index 4abda508f5..18a1b1c4bf 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseRequest.java @@ -32,6 +32,9 @@ public class LicenseRequest { @JsonProperty("product") private String product; + + @JsonProperty("time_zone_id") + private String timeZoneId; public String getHostId() { return hostId; @@ -59,6 +62,16 @@ public class LicenseRequest { this.product = product; return this; } + + public String getTimeZoneId() { + return timeZoneId; + } + + public LicenseRequest setTimeZoneId(String timeZoneId) { + this.timeZoneId = timeZoneId; + return this; + } + } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java index 4d68f86e5e..a3a8247884 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/LicenseResponse.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ @JsonIgnoreProperties(ignoreUnknown = true) public class LicenseResponse { + private final Boolean success; private final Boolean hostChanged; private final Long hostChangesRemaining; @@ -34,9 +35,9 @@ public class LicenseResponse { @JsonCreator public LicenseResponse( - @JsonProperty("success") Boolean success, - @JsonProperty("hostChanged") Boolean hostChanged, - @JsonProperty("hostChangesRemaining") Long hostChangesRemaining, + @JsonProperty("success") Boolean success, + @JsonProperty("hostChanged") Boolean hostChanged, + @JsonProperty("hostChangesRemaining") Long hostChangesRemaining, @JsonProperty("boostLicense") BoostLicenseResponse boostLicense ) { this.success = success; @@ -60,7 +61,4 @@ public class LicenseResponse { public BoostLicenseResponse getBoostLicense() { return boostLicense; } - - - } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index ac6fe958ab..d6412fff67 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -57,11 +57,11 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { private static final DateTimeFormatter LICENSE_EXPIRES_FORMAT = DateTimeFormatter .ofPattern("MMMM d, YYYY") - .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); + .withZone(ZoneId.of(UserPreferences.getInferredUserTimeZone())); private static final DateTimeFormatter MALWARE_SCANS_RESET_FORMAT = DateTimeFormatter .ofPattern("MMM d, YYYY' at 'h:mma") - .withZone(ZoneId.of(UserPreferences.getTimeZoneForDisplays())); + .withZone(ZoneId.of(UserPreferences.getInferredUserTimeZone())); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); private final CTLicensePersistence ctPersistence = CTLicensePersistence.getInstance(); @@ -406,11 +406,16 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.licenseInfoUserLabel.setVisible(false); } else { this.licenseInfoExpiresLabel.setVisible(true); - this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires(this.licenseInfo.getDecryptedLicense().getExpirationDate() == null ? "" : LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); + this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires( + this.licenseInfo.getDecryptedLicense().getExpirationDate() == null + ? "" + : LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); this.licenseInfoIdLabel.setVisible(true); this.licenseInfoIdLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_id(this.licenseInfo.getDecryptedLicense().getBoostLicenseId())); this.licenseInfoUserLabel.setVisible(true); - this.licenseInfoUserLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_userInfo(this.licenseInfo.getUser(), this.licenseInfo.getEmail())); + this.licenseInfoUserLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_userInfo( + "TBD", + this.licenseInfo.getDecryptedLicense().getCustomerEmail())); } this.malwareScansPanel.setVisible(StringUtils.isNotBlank(this.authTokenMessage) || authTokenResponse != null); @@ -430,7 +435,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.maxFileUploadsLabel.setVisible(true); this.maxFileUploadsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups(this.authTokenResponse.getFileUploadLimit())); this.countersResetLabel.setVisible(true); - this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(this.authTokenResponse.getExpiration() == null ? "" : MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getExpiration()))); + this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(this.authTokenResponse.getResetDate() == null ? "" : MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getResetDate()))); this.hashLookupsRemainingLabel.setVisible(true); this.hashLookupsRemainingLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining(remaining(this.authTokenResponse.getHashLookupLimit(), this.authTokenResponse.getHashLookupCount()))); this.fileUploadsRemainingLabel.setVisible(true); diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index cee09547a3..da9e2629f1 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Paths; +import java.time.ZoneId; import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -385,6 +386,30 @@ public final class UserPreferences { public static String getTimeZoneForDisplays() { return viewPreferences.get(TIME_ZONE_FOR_DISPLAYS, TimeZone.GMT_ZONE.getID()); } + + /** + * Returns the inferred preferred time zone deriving in order: + * 1) Starts with user preference if set + * 2) Gets system time zone if can be determined + * 3) Otherwise uses GMT + * + * @return Returns preferred time zone id. + */ + public static String getInferredUserTimeZone() { + String timeZonePref = viewPreferences.get(TIME_ZONE_FOR_DISPLAYS, null); + if (StringUtils.isBlank(timeZonePref)) { + ZoneId systemZoneId = ZoneId.systemDefault(); + if (systemZoneId != null) { + timeZonePref = systemZoneId.getId(); + } + } + + if (StringUtils.isBlank(timeZonePref)) { + timeZonePref = TimeZone.GMT_ZONE.getID(); + } + + return timeZonePref; + } public static void setTimeZoneForDisplays(String timeZone) { viewPreferences.put(TIME_ZONE_FOR_DISPLAYS, timeZone); From 67dd48307b2d03fff7d6eeb02c13acccc25ce2a2 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 11:18:01 -0400 Subject: [PATCH 17/29] updates for EULA loading --- .../autopsy/ctapi/{CtApiDAO.java => CTApiDAO.java} | 0 .../ctcloud/CTMalwareScannerOptionsPanel.java | 1 + .../autopsy/ctoptions/ctcloud/EULADialog.form | 8 -------- .../autopsy/ctoptions/ctcloud/EULADialog.java | 10 ++++++---- 4 files changed, 7 insertions(+), 12 deletions(-) rename Core/src/com/basistech/df/cybertriage/autopsy/ctapi/{CtApiDAO.java => CTApiDAO.java} (100%) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTApiDAO.java similarity index 100% rename from Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CtApiDAO.java rename to Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTApiDAO.java diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index d6412fff67..d68168c3b6 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -167,6 +167,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { this.licenseFetcher.cancel(true); } setLicenseDisplay(null, Bundle.CTOPtionsPanel_loadLicenseInfo_loading()); + setMalwareScansDisplay(null, null); this.licenseFetcher = new LicenseFetcher(licenseNumber); this.licenseFetcher.execute(); } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form index cf95ed8920..ea67d1f7d7 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.form @@ -36,14 +36,6 @@ - - - - - - - - diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java index 75d7af3248..f5c22e1b22 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import javafx.application.Platform; +import javafx.concurrent.Worker.State; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.layout.VBox; @@ -52,12 +53,15 @@ public class EULADialog extends javax.swing.JDialog { webView.setMaxSize(Short.MAX_VALUE, Short.MAX_VALUE); webView.setPrefSize(Short.MAX_VALUE, Short.MAX_VALUE); webView.setMinSize(100, 100); + webView.getEngine().getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> { + if (newState == State.SUCCEEDED) { + SwingUtilities.invokeLater(() -> EULADialog.this.acceptButton.setEnabled(true)); + } + }); webView.getEngine().loadContent(htmlString, "text/html"); VBox root = new VBox(webView); Scene scene = new Scene(root, Color.RED); fxPanel.setScene(scene); - - SwingUtilities.invokeLater(() -> EULADialog.this.acceptButton.setEnabled(true)); }); } @@ -83,8 +87,6 @@ public class EULADialog extends javax.swing.JDialog { setSize(new java.awt.Dimension(550, 550)); getContentPane().setLayout(new java.awt.GridBagLayout()); - viewablePanel.setMaximumSize(new java.awt.Dimension(32767, 32767)); - viewablePanel.setPreferredSize(null); viewablePanel.setLayout(new java.awt.BorderLayout()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; From 9b59d2c658eb5ebf834af38114b86149dae748ae Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 11:42:22 -0400 Subject: [PATCH 18/29] license update --- .../autopsy/ctoptions/ctcloud/EULADialog.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java index f5c22e1b22..8b93e80c50 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java @@ -1,6 +1,20 @@ /* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JDialog.java to edit this template + * Autopsy Forensic Browser + * + * Copyright 2023 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. */ package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; From 404284cdfcddc029c57ea7602815007c7ab0739c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 11:53:22 -0400 Subject: [PATCH 19/29] fix for save license --- .../autopsy/ctoptions/ctcloud/CTLicensePersistence.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java index 784e15234b..9f7f3b8bf6 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicensePersistence.java @@ -56,11 +56,7 @@ public class CTLicensePersistence { File licenseFile = getCTLicenseFile(); try { licenseFile.getParentFile().mkdirs(); - if (licenseResponse != null) { - objectMapper.writeValue(licenseFile, licenseResponse); - } else if (licenseFile.exists()) { - Files.delete(licenseFile.toPath()); - } + objectMapper.writeValue(licenseFile, licenseResponse); return true; } catch (IOException ex) { logger.log(Level.WARNING, "There was an error writing CyberTriage license to file: " + licenseFile.getAbsolutePath(), ex); From a634a2e7fd3729132e28f7b306f17e1073131f8f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 24 Jul 2023 15:49:57 -0400 Subject: [PATCH 20/29] dont rescan --- .../malwarescan/MalwareScanIngestModule.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index beffd8e594..a760c3bbe3 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** @@ -198,12 +199,21 @@ public class MalwareScanIngestModule implements FileIngestModule { }) IngestModule.ProcessResult process(AbstractFile af) { try { - if (runState == RunState.STARTED_UP && af.getKnown() != TskData.FileKnown.KNOWN - && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.getMIMEType(af)).trim().toLowerCase())) { + if (runState == RunState.STARTED_UP + && af.getKnown() != TskData.FileKnown.KNOWN + && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.getMIMEType(af)).trim().toLowerCase()) + && CollectionUtils.isEmpty(af.getAnalysisResults(malwareType))) { + batchProcessor.add(new FileRecord(af.getId(), af.getMd5Hash())); } return ProcessResult.OK; + } catch (TskCoreException ex) { + notifyWarning( + Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_title(), + Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc(), + ex); + return IngestModule.ProcessResult.ERROR; } catch (InterruptedException ex) { notifyWarning( Bundle.MalwareScanIngestModule_ShareProcessing_batchTimeout_title(), @@ -231,7 +241,7 @@ public class MalwareScanIngestModule implements FileIngestModule { // create mapping of md5 to corresponding object ids as well as just the list of md5's Map> md5ToObjId = new HashMap<>(); - List md5Hashes = new ArrayList<>(); + for (FileRecord fr : fileRecords) { if (fr == null || StringUtils.isBlank(fr.getMd5hash()) || fr.getObjId() <= 0) { continue; @@ -242,9 +252,10 @@ public class MalwareScanIngestModule implements FileIngestModule { .computeIfAbsent(sanitizedMd5, (k) -> new ArrayList<>()) .add(fr.getObjId()); - md5Hashes.add(sanitizedMd5); } + List md5Hashes = new ArrayList<>(md5ToObjId.keySet()); + if (md5Hashes.isEmpty()) { return; } From a7f6cc817951101220c3365da174d5fa59a6cbc9 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Jul 2023 12:27:02 -0400 Subject: [PATCH 21/29] error handling --- .../autopsy/ctoptions/LicenseDisclaimerPanel.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java index ffd3760e6f..4299f02d62 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/LicenseDisclaimerPanel.java @@ -22,16 +22,18 @@ import java.awt.Desktop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; /** * Disclaimer for license and place to purchase CT license. */ public class LicenseDisclaimerPanel extends javax.swing.JPanel { + private static final Logger LOGGER = Logger.getLogger(LicenseDisclaimerPanel.class.getName()); - + private static final String CHECKOUT_PAGE_URL = "https://cybertriage.com/autopsy-checkout"; - + /** * Creates new form LicenseDisclaimerPanel */ @@ -116,9 +118,10 @@ public class LicenseDisclaimerPanel extends javax.swing.JPanel { try { Desktop.getDesktop().browse(new URI(CHECKOUT_PAGE_URL)); } catch (IOException | URISyntaxException e) { - /* TODO: error handling */ } + LOGGER.log(Level.SEVERE, "Error opening link to: " + CHECKOUT_PAGE_URL, e); + } } else { - /* TODO: error handling */ + LOGGER.log(Level.WARNING, "Desktop API is not supported. Link cannot be opened."); } }//GEN-LAST:event_linkMouseClicked From 07aae2521bc00401973dffe6f8c3cd9ceade8e7a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Jul 2023 14:04:10 -0400 Subject: [PATCH 22/29] license validation change --- .../autopsy/ctoptions/ctcloud/CTLicenseDialog.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java index 11af572890..608ea63040 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTLicenseDialog.java @@ -29,9 +29,7 @@ import org.openide.util.NbBundle.Messages; */ public class CTLicenseDialog extends javax.swing.JDialog { - //private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[0-9a-zA-Z]{8}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{4}\\-[0-9a-zA-Z]{12}\\s*$"); - - private static final Pattern LICENSE_PATTERN = Pattern.compile("\\s*[a-zA-Z0-9\\-]+?\\s*"); + private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[a-zA-Z0-9\\-]+?\\s*$"); private String licenseString = null; /** From 76915e9e05d21302ebd50522f7f115f87d1f38e2 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Jul 2023 15:03:59 -0400 Subject: [PATCH 23/29] fixes --- .../autopsy/malwarescan/BatchProcessor.java | 58 ++++++---------- .../malwarescan/MalwareScanIngestModule.java | 67 +++++++++++-------- 2 files changed, 59 insertions(+), 66 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java index dc62f6a05e..eab025a641 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java @@ -38,27 +38,18 @@ import java.util.function.Consumer; */ public class BatchProcessor { - private final ExecutorService processingExecutorService = Executors.newSingleThreadExecutor(); + private ExecutorService processingExecutorService = Executors.newSingleThreadExecutor(); private final BlockingQueue batchingQueue; - private final List processingQueue; private final int batchSize; private final Consumer> itemsConsumer; - private final long millisTimeout; + private final long secondsTimeout; - private Future lastProcessingFuture = CompletableFuture.runAsync(() -> { - }); - - public BatchProcessor(int batchSize, long millisTimeout, Consumer> itemsConsumer) { + public BatchProcessor(int batchSize, long secondsTimeout, Consumer> itemsConsumer) { this.batchingQueue = new LinkedBlockingQueue<>(batchSize); - this.processingQueue = new ArrayList<>(batchSize); this.batchSize = batchSize; this.itemsConsumer = itemsConsumer; - this.millisTimeout = millisTimeout; - } - - public synchronized void clearCurrentBatch() { - batchingQueue.clear(); + this.secondsTimeout = secondsTimeout; } public synchronized void add(T item) throws InterruptedException { @@ -68,40 +59,29 @@ public class BatchProcessor { } } - public synchronized void flush(boolean blockUntilFinished) throws InterruptedException { + public synchronized void flushAndReset() throws InterruptedException { + // get any remaining asyncProcessBatch(); - if (blockUntilFinished) { - waitCurrentFuture(); - } - } - - private synchronized void waitCurrentFuture() throws InterruptedException { - synchronized (lastProcessingFuture) { - if (!lastProcessingFuture.isDone()) { - try { - lastProcessingFuture.get(millisTimeout, TimeUnit.MILLISECONDS); - } catch (ExecutionException | TimeoutException ex) { - // ignore timeout - } - } - } + + // don't accept any new additions + processingExecutorService.shutdown(); + + // await termination + processingExecutorService.awaitTermination(secondsTimeout, TimeUnit.SECONDS); + + // get new (not shut down executor) + processingExecutorService = Executors.newSingleThreadExecutor(); } private synchronized void asyncProcessBatch() throws InterruptedException { if (!batchingQueue.isEmpty()) { - // wait for previous processing to finish - waitCurrentFuture(); - - // if 'andThen' doesn't run, clear the processing queue - processingQueue.clear(); + final List processingList = new ArrayList<>(); // transfer batching queue to processing queue - batchingQueue.drainTo(processingQueue); + batchingQueue.drainTo(processingList); - // submit to processor and then clear processing queue - lastProcessingFuture = processingExecutorService.submit( - () -> itemsConsumer.andThen(processingQueue -> processingQueue.clear()).accept(processingQueue) - ); + // submit to be processed + processingExecutorService.submit(() -> itemsConsumer.accept(processingList)); } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java index a760c3bbe3..6d3f7d4766 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/MalwareScanIngestModule.java @@ -24,6 +24,7 @@ import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthenticatedRequestData; import com.basistech.df.cybertriage.autopsy.ctapi.json.CTCloudBean; import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo; import com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud.CTLicensePersistence; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -48,6 +49,9 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.HashUtility.HashResult; +import org.sleuthkit.datamodel.HashUtility.HashType; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -83,8 +87,8 @@ public class MalwareScanIngestModule implements FileIngestModule { // batch size of 200 files max private static final int BATCH_SIZE = 200; - // 3 minute timeout for an API request - private static final long BATCH_MILLIS_TIMEOUT = 3 * 60 * 1000; + // 1 day timeout for all API requests + private static final long FLUSH_SECS_TIMEOUT = 24 * 60 * 60; //minimum lookups left before issuing warning private static final long LOW_LOOKUPS_REMAINING = 250; @@ -107,13 +111,13 @@ public class MalwareScanIngestModule implements FileIngestModule { private static final String MALWARE_CONFIG = "Cyber Triage Cloud"; private static final Logger logger = Logger.getLogger(MalwareScanIngestModule.class.getName()); - private final BatchProcessor batchProcessor = new BatchProcessor(BATCH_SIZE, BATCH_MILLIS_TIMEOUT, this::handleBatch); + private final BatchProcessor batchProcessor = new BatchProcessor(BATCH_SIZE, FLUSH_SECS_TIMEOUT, this::handleBatch); private final CTLicensePersistence ctSettingsPersistence = CTLicensePersistence.getInstance(); private final CTApiDAO ctApiDAO = CTApiDAO.getInstance(); private RunState runState = null; - + private SleuthkitCase tskCase = null; private FileTypeDetector fileTypeDetector = null; private LicenseInfo licenseInfo = null; @@ -193,19 +197,46 @@ public class MalwareScanIngestModule implements FileIngestModule { return limit - used; } + private String getOrCalcHash(AbstractFile af) { + if (StringUtils.isNotBlank(af.getMd5Hash())) { + return af.getMd5Hash(); + } + + try { + List hashResults = HashUtility.calculateHashes(af, Collections.singletonList(HashType.MD5)); + if (CollectionUtils.isNotEmpty(hashResults)) { + for (HashResult hashResult : hashResults) { + if (hashResult.getType() == HashType.MD5) { + return hashResult.getValue(); + } + } + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, + MessageFormat.format("An error occurred while processing file name: {0} and obj id: {1}.", + af.getName(), + af.getId()), + ex); + } + + return null; + } + @Messages({ "MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Timeout", "MalwareScanIngestModule_ShareProcessing_batchTimeout_desc=Batch processing timed out" }) IngestModule.ProcessResult process(AbstractFile af) { try { - if (runState == RunState.STARTED_UP + if (runState == RunState.STARTED_UP && af.getKnown() != TskData.FileKnown.KNOWN && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.getMIMEType(af)).trim().toLowerCase()) && CollectionUtils.isEmpty(af.getAnalysisResults(malwareType))) { - - batchProcessor.add(new FileRecord(af.getId(), af.getMd5Hash())); + String md5 = getOrCalcHash(af); + if (StringUtils.isNotBlank(md5)) { + batchProcessor.add(new FileRecord(af.getId(), md5)); + } } return ProcessResult.OK; } catch (TskCoreException ex) { @@ -255,7 +286,7 @@ public class MalwareScanIngestModule implements FileIngestModule { } List md5Hashes = new ArrayList<>(md5ToObjId.keySet()); - + if (md5Hashes.isEmpty()) { return; } @@ -275,13 +306,6 @@ public class MalwareScanIngestModule implements FileIngestModule { return; } - // if the size of this batch will exceed limit, shrink list to limit and fail after processing - boolean exceededScanLimit = false; - if (remainingScans < md5Hashes.size()) { - md5Hashes = md5Hashes.subList(0, (int) remainingScans); - exceededScanLimit = true; - } - // using auth token, get results List repResult = ctApiDAO.getReputationResults( new AuthenticatedRequestData(licenseInfo.getDecryptedLicense(), authTokenResponse), @@ -321,16 +345,6 @@ public class MalwareScanIngestModule implements FileIngestModule { if (!CollectionUtils.isEmpty(createdArtifacts)) { tskCase.getBlackboard().postArtifacts(createdArtifacts, Bundle.MalwareScanIngestModuleFactory_displayName(), ingestJobId); } - - // if we only processed part of the batch, after processing, notify that we are out of scans. - if (exceededScanLimit) { - runState = RunState.DISABLED; - notifyWarning( - Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(), - Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(), - null); - return; - } } } catch (Exception ex) { notifyWarning( @@ -387,7 +401,7 @@ public class MalwareScanIngestModule implements FileIngestModule { // flush any remaining items try { - batchProcessor.flush(true); + batchProcessor.flushAndReset(); } catch (InterruptedException ex) { notifyWarning( Bundle.MalwareScanIngestModule_SharedProcessing_flushTimeout_title(), @@ -396,7 +410,6 @@ public class MalwareScanIngestModule implements FileIngestModule { } finally { // set state to shut down and clear any remaining runState = RunState.SHUT_DOWN; - batchProcessor.clearCurrentBatch(); } } From bed475ae5ab6daad08e9326058f88448bc0ed867 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Jul 2023 15:38:05 -0400 Subject: [PATCH 24/29] update for urls --- .../df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java | 4 +++- .../com/basistech/df/cybertriage/autopsy/ctapi/Constants.java | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java index d774ff1661..533739201a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -70,6 +70,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.SystemDefaultCredentialsProvider; import org.apache.http.impl.client.WinHttpClients; +import org.sleuthkit.autopsy.coreutils.Version; /** * Actually makes the http requests to CT cloud. @@ -78,6 +79,7 @@ public class CTCloudHttpClient { private static final CTCloudHttpClient instance = new CTCloudHttpClient(); private static final Logger LOGGER = Logger.getLogger(CTCloudHttpClient.class.getName()); + private static final String HOST_URL = Version.getBuildType() == Version.Type.RELEASE ? Constants.CT_CLOUD_SERVER : Constants.CT_CLOUD_DEV_SERVER; private static final List DEFAULT_SCHEME_PRIORITY = new ArrayList<>(Arrays.asList( @@ -137,7 +139,7 @@ public class CTCloudHttpClient { } public O doPost(String urlPath, Map urlReqParams, Object jsonBody, Class classType) throws CTCloudException { - String url = Constants.CT_CLOUD_SERVER + urlPath; + String url = HOST_URL + urlPath; try { LOGGER.log(Level.INFO, "initiating http connection to ctcloud server"); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java index fa35dd7dbb..ddda5b4508 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -34,8 +34,7 @@ final public class Constants { public static final String CT_CLOUD_DEV_SERVER = "https://cyber-triage-dev.appspot.com"; - // TODO put back - public static final String CT_CLOUD_SERVER = CT_CLOUD_DEV_SERVER; //"https://rep1.cybertriage.com"; + public static final String CT_CLOUD_SERVER = "https://rep1.cybertriage.com"; /** * Link to watch demo video From d1ad7e06e0b6798aa54b8316931b22106ec6c865 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 25 Jul 2023 16:32:59 -0400 Subject: [PATCH 25/29] name update --- .../df/cybertriage/autopsy/ctoptions/Bundle.properties | 4 ++-- .../df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties index 68677cb3f7..46f62fdf7a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties @@ -1,8 +1,8 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template -OptionsCategory_Name_CyberTriage=CyberTriage -OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage +OptionsCategory_Name_CyberTriage=Cyber Triage +OptionsCategory_Keywords_CyberTriage=Cyber Triage,Cyber,Triage LicenseDisclaimerPanel.disclaimer.text=The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use. LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from LicenseDisclaimerPanel.link.text=https://cybertriage.com/autopsy-checkout diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED index 68677cb3f7..46f62fdf7a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties-MERGED @@ -1,8 +1,8 @@ # Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license # Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template -OptionsCategory_Name_CyberTriage=CyberTriage -OptionsCategory_Keywords_CyberTriage=CyberTriage,Cyber,Triage +OptionsCategory_Name_CyberTriage=Cyber Triage +OptionsCategory_Keywords_CyberTriage=Cyber Triage,Cyber,Triage LicenseDisclaimerPanel.disclaimer.text=The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use. LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from LicenseDisclaimerPanel.link.text=https://cybertriage.com/autopsy-checkout From 154a5ea4068c0c593aafb983dd35ea102392aa37 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Jul 2023 08:48:51 -0400 Subject: [PATCH 26/29] update for customer name --- .../autopsy/ctapi/json/DecryptedLicenseResponse.java | 9 ++++++++- .../ctcloud/CTMalwareScannerOptionsPanel.java | 12 ++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java index 5be0996a9e..c6f91721ef 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/json/DecryptedLicenseResponse.java @@ -41,6 +41,7 @@ public class DecryptedLicenseResponse { private final String limitType; private final String timezone; private final String customerEmail; + private final String customerName; @JsonCreator public DecryptedLicenseResponse( @@ -55,7 +56,8 @@ public class DecryptedLicenseResponse { @JsonProperty("product") String product, @JsonProperty("limitType") String limitType, @JsonProperty("timezone") String timezone, - @JsonProperty("customerEmail") String customerEmail + @JsonProperty("customerEmail") String customerEmail, + @JsonProperty("customerName") String customerName ) { this.boostLicenseId = boostLicenseId; this.licenseHostId = licenseHostId; @@ -67,6 +69,7 @@ public class DecryptedLicenseResponse { this.limitType = limitType; this.timezone = timezone; this.customerEmail = customerEmail; + this.customerName = customerName; } public String getBoostLicenseId() { @@ -108,4 +111,8 @@ public class DecryptedLicenseResponse { public String getCustomerEmail() { return customerEmail; } + + public String getCustomerName() { + return customerName; + } } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index d68168c3b6..b9757e69a8 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -408,15 +408,15 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { } else { this.licenseInfoExpiresLabel.setVisible(true); this.licenseInfoExpiresLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_expires( - this.licenseInfo.getDecryptedLicense().getExpirationDate() == null - ? "" - : LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); + this.licenseInfo.getDecryptedLicense().getExpirationDate() == null + ? "" + : LICENSE_EXPIRES_FORMAT.format(this.licenseInfo.getDecryptedLicense().getExpirationDate()))); this.licenseInfoIdLabel.setVisible(true); - this.licenseInfoIdLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_id(this.licenseInfo.getDecryptedLicense().getBoostLicenseId())); + this.licenseInfoIdLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_id(StringUtils.defaultString(this.licenseInfo.getDecryptedLicense().getBoostLicenseId()))); this.licenseInfoUserLabel.setVisible(true); this.licenseInfoUserLabel.setText(Bundle.CTMalwareScannerOptionsPanel_licenseInfo_userInfo( - "TBD", - this.licenseInfo.getDecryptedLicense().getCustomerEmail())); + StringUtils.defaultString(this.licenseInfo.getDecryptedLicense().getCustomerName()), + StringUtils.defaultString(this.licenseInfo.getDecryptedLicense().getCustomerEmail()))); } this.malwareScansPanel.setVisible(StringUtils.isNotBlank(this.authTokenMessage) || authTokenResponse != null); From 2126d4b69c4d4d640b354afbfdffd50d167ba6e7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Jul 2023 09:26:31 -0400 Subject: [PATCH 27/29] public package for use by nbms --- Core/nbproject/project.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 2cab2a535f..9b960955aa 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -323,6 +323,7 @@ + com.basistech.df.cybertriage.autopsy.ctoptions.subpanel net.sf.sevenzipjbinding net.sf.sevenzipjbinding.impl net.sf.sevenzipjbinding.simple From 8b536c9a11e7934374054cd3d5afedae0df51200 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Jul 2023 14:56:41 -0400 Subject: [PATCH 28/29] update for ibiblio --- Core/ivysettings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/ivysettings.xml b/Core/ivysettings.xml index fd792f6844..06c2d9308f 100644 --- a/Core/ivysettings.xml +++ b/Core/ivysettings.xml @@ -4,6 +4,7 @@ + From 86aabfae841742aca7ae9c93b02b0c9020e46dee Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 26 Jul 2023 15:08:10 -0400 Subject: [PATCH 29/29] comment updates --- .../cybertriage/autopsy/ctapi/CTCloudException.java | 6 ++---- .../cybertriage/autopsy/ctapi/CTCloudHttpClient.java | 12 +++++------- .../df/cybertriage/autopsy/ctapi/Constants.java | 3 +++ .../df/cybertriage/autopsy/ctapi/ProxySettings.java | 2 -- .../autopsy/ctapi/util/ObjectMapperUtil.java | 4 ---- .../autopsy/ctoptions/CTOptionsPanel.java | 5 +++-- .../ctcloud/CTMalwareScannerOptionsPanel.java | 5 +++-- .../autopsy/ctoptions/ctcloud/EULADialog.java | 3 +-- 8 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java index 698afc3297..52d586ee53 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudException.java @@ -24,11 +24,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; /** - * - * @author rishwanth + * An exception thrown due to an error that occurs while making a CT Cloud REST + * API request. */ - - public class CTCloudException extends Exception{ private final ErrorCode errorCode; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java index 533739201a..9d4b189ee9 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java @@ -73,7 +73,7 @@ import org.apache.http.impl.client.WinHttpClients; import org.sleuthkit.autopsy.coreutils.Version; /** - * Actually makes the http requests to CT cloud. + * Makes the http requests to CT cloud. */ public class CTCloudHttpClient { @@ -263,12 +263,10 @@ public class CTCloudHttpClient { } /** - * Creates and returns a CloseableHttpClient SYSTEM and MANUAL looks up from - * runtime proxy config settings. These are updated accordingly from the - * Proxy Config UI. This allows us to keep the CreateConnection call fairly - * simple and not have to deal with the System Proxy settings and such. - * - * @return + * Creates a connection to CT Cloud with the given arguments. + * @param proxySettings The network proxy settings. + * @param sslContext The ssl context or null. + * @return The connection to CT Cloud. */ private static CloseableHttpClient createConnection(ProxySettingArgs proxySettings, SSLContext sslContext) { HttpClientBuilder builder = getHttpClientBuilder(proxySettings); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java index ddda5b4508..9587b3cd44 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/Constants.java @@ -20,6 +20,9 @@ package com.basistech.df.cybertriage.autopsy.ctapi; import java.net.URI; +/** + * Constants regarding connections to cyber triage cloud. + */ final public class Constants { public static final String CYBER_TRIAGE = "CyberTriage"; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java index 2d3f0d667f..f710a6ab1b 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/ProxySettings.java @@ -18,8 +18,6 @@ */ package com.basistech.df.cybertriage.autopsy.ctapi; - - import java.net.*; import java.util.*; import java.util.logging.Level; diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java index d59d8f8307..5562141255 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/util/ObjectMapperUtil.java @@ -26,8 +26,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; @@ -37,7 +35,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; -import java.util.Date; import java.util.Locale; import java.util.function.Function; @@ -58,7 +55,6 @@ public class ObjectMapperUtil { public ObjectMapper getDefaultObjectMapper() { ObjectMapper defaultMapper = new ObjectMapper(); - // defaultMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); defaultMapper.registerModule(new JavaTimeModule()); return defaultMapper; } diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java index 6eb72dd73e..745aa1d03d 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java @@ -35,7 +35,7 @@ import org.openide.util.Lookup; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; /** - * Options panel for CyberTriage. + * Options panel for Cyber Triage. */ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { @@ -46,7 +46,8 @@ public class CTOptionsPanel extends IngestModuleGlobalSettingsPanel { private final List subPanels; /** - * Creates new form CTOptions + * Creates new form CTOptions loading any CTOptionsSubPanel instances to be + * displayed. */ public CTOptionsPanel() { initComponents(); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java index b9757e69a8..564eeb7eba 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java @@ -48,7 +48,8 @@ import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.core.UserPreferences; /** - * Options panel for CyberTriage options for importing a CyberTriage incident + * Options panel to be displayed in the CTOptionsPanel for settings regarding + * Cyber Triage Malware Scanner settings and license setup. */ @ServiceProvider(service = CTOptionsSubPanel.class) public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { @@ -75,7 +76,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel { private volatile AuthTokenFetcher authTokenFetcher = null; /** - * Creates new form CTIncidentImportOptionsPanel + * Main constructor. */ public CTMalwareScannerOptionsPanel() { initComponents(); diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java index 8b93e80c50..31056e1f2a 100644 --- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java +++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/EULADialog.java @@ -34,8 +34,7 @@ import org.apache.commons.io.IOUtils; import org.sleuthkit.autopsy.coreutils.Logger; /** - * - * @author gregd + * Dialog for displaying the Cyber Triage EULA before the license is saved. */ public class EULADialog extends javax.swing.JDialog {