From 0c3394e88f6909ccb1f8176485798cf739a06cf6 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro <gregd@basistech.com> Date: Thu, 20 Jul 2023 14:07:52 -0400 Subject: [PATCH] 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 @@ +<!DOCTYPE ivy-module [ + <!ENTITY httpcomponents.version "4.5.14"> +]> <ivy-module version="2.0"> <info organisation="org.sleuthkit.autopsy" module="core"/> <configurations > @@ -72,6 +75,15 @@ <!-- annotations like guarded by --> <dependency conf="core->default" org="com.github.spotbugs" name="spotbugs-annotations" rev="4.6.0"/> + <dependency conf="core->default" org="com.license4j" name="license4j-runtime-library" rev="4.7.1"/> + + <dependency conf="core->default" org="org.apache.httpcomponents" name="httpclient" rev="&httpcomponents.version;"/> + <dependency conf="core->default" org="org.apache.httpcomponents" name="httpmime" rev="&httpcomponents.version;"/> + <dependency conf="core->default" org="org.apache.httpcomponents" name="httpclient-win" rev="&httpcomponents.version;"> + <exclude name="jna" /> + <exclude name="jna-platform" /> + </dependency> + <override org="org.apache.zookeeper" module="zookeeper" rev="3.8.0"/> <override org="org.apache.zookeeper" module="zookeeper-jute" rev="3.8.0"/> @@ -84,5 +96,6 @@ <override org="org.bouncycastle" module="bcprov-ext-jdk15on" rev="1.70"/> <override org="org.bouncycastle" module="bcprov-jdk15on" rev="1.70"/> <override org="org.bouncycastle" module="bcpkix-jdk15on" rev="1.70"/> + <override org="junit" module="junit" rev="4.13.2"/> </dependencies> </ivy-module> 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 @@ <implementation-version/> </run-dependency> </dependency> + <dependency> + <code-name-base>org.netbeans.modules.keyring</code-name-base> + <build-prerequisite/> + <compile-dependency/> + <run-dependency> + <specification-version>1.41</specification-version> + </run-dependency> + </dependency> <dependency> <code-name-base>org.netbeans.modules.options.api</code-name-base> <build-prerequisite/> @@ -165,14 +173,6 @@ <specification-version>9.29</specification-version> </run-dependency> </dependency> - <!-- <dependency> - <code-name-base>org.openide.filesystems.compat8</code-name-base> - <build-prerequisite/> - <compile-dependency/> - <run-dependency> - <specification-version>9.26</specification-version> - </run-dependency> - </dependency> --> <dependency> <code-name-base>org.openide.filesystems.nb</code-name-base> <build-prerequisite/> @@ -448,6 +448,10 @@ <runtime-relative-path>ext/checker-qual-3.33.0.jar</runtime-relative-path> <binary-origin>release/modules/ext/checker-qual-3.33.0.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/commons-codec-1.11.jar</runtime-relative-path> + <binary-origin>release/modules/ext/commons-codec-1.11.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/commons-dbcp2-2.9.0.jar</runtime-relative-path> <binary-origin>release/modules/ext/commons-dbcp2-2.9.0.jar</binary-origin> @@ -500,6 +504,22 @@ <runtime-relative-path>ext/guava-32.0.1-jre.jar</runtime-relative-path> <binary-origin>release/modules/ext/guava-32.0.1-jre.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpclient-4.5.14.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpclient-4.5.14.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpclient-win-4.5.14.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpclient-win-4.5.14.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpcore-4.4.16.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpcore-4.4.16.jar</binary-origin> + </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/httpmime-4.5.14.jar</runtime-relative-path> + <binary-origin>release/modules/ext/httpmime-4.5.14.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/icepdf-core-6.2.2.jar</runtime-relative-path> <binary-origin>release/modules/ext/icepdf-core-6.2.2.jar</binary-origin> @@ -560,6 +580,10 @@ <runtime-relative-path>ext/jaxb-runtime-2.3.3.jar</runtime-relative-path> <binary-origin>release/modules/ext/jaxb-runtime-2.3.3.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jdom-2.0.5-contrib.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jdom-2.0.5-contrib.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path> <binary-origin>release/modules/ext/jdom-2.0.5.jar</binary-origin> @@ -596,6 +620,10 @@ <runtime-relative-path>ext/libphonenumber-8.12.45.jar</runtime-relative-path> <binary-origin>release/modules/ext/libphonenumber-8.12.45.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/license4j-runtime-library-4.7.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/license4j-runtime-library-4.7.1.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/listenablefuture-1.0.jar</runtime-relative-path> <binary-origin>release/modules/ext/listenablefuture-1.0.jar</binary-origin> 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> T doPost(String urlPath, Object requestBody, Class<T> 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<FileReputationResult> getReputationResults(String authToken, List<String> 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<String> set = new HashSet<String> (); + 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<Proxy> 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 <code>o.n.core</code> and <code>core.network</code>. + * An implementation of this class brings a facility to reload Network Proxy Settings + * from underlying OS. + * The module <code>core.network</code> provides a implementation which may be accessible + * via <code>Lookup.getDefault()</code>. 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<MetadataLabel> 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<MetadataLabel> 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<MetadataLabel> 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 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> + <AuxValues> + <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> + <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> + <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,120,0,0,2,2"/> + </AuxValues> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> + <SubComponents> + <Container class="javax.swing.JScrollPane" name="scrollPane"> + <AuxValues> + <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> + <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> + </AuxValues> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> + <BorderConstraints direction="Center"/> + </Constraint> + </Constraints> + + <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> + <SubComponents> + <Container class="javax.swing.JPanel" name="contentPane"> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> + </Container> + </SubComponents> + </Container> + </SubComponents> +</Form> 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<CTOptionsSubPanel> subPanels; + + /** + * Creates new form CTOptions + */ + public CTOptionsPanel() { + initComponents(); + Collection<? extends CTOptionsSubPanel> coll = Lookup.getDefault().lookupAll(CTOptionsSubPanel.class); + Stream<? extends CTOptionsSubPanel> 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<CTOptionsSubPanel> 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") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//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); + }// </editor-fold>//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=<html>Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'</html> +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=<html>User: {0}<br/>Email: {1}</html> +# {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 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo"> + <Properties> + <Property name="defaultCloseOperation" type="int" value="2"/> + <Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="CTLicenseDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="alwaysOnTop" type="boolean" value="true"/> + <Property name="resizable" type="boolean" value="false"/> + </Properties> + <SyntheticProperties> + <SyntheticProperty name="formSizePolicy" type="int" value="1"/> + <SyntheticProperty name="generateCenter" type="boolean" value="false"/> + </SyntheticProperties> + <AuxValues> + <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> + <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> + <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,122,0,0,1,-19"/> + </AuxValues> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> + <SubComponents> + <Component class="javax.swing.JLabel" name="licenseNumberLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.licenseNumberLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> + <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> + </AuxValues> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="0" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="warningLabel"> + <Properties> + <Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> + <Connection code="java.awt.Color.RED" type="code"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.warningLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[419, 36]"/> + </Property> + <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[419, 36]"/> + </Property> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[419, 36]"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="2" gridWidth="3" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Container class="javax.swing.JPanel" name="buttonPadding"> + <AuxValues> + <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> + <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> + </AuxValues> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + + <Layout> + <DimensionLayout dim="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + </DimensionLayout> + <DimensionLayout dim="1"> + <Group type="103" groupAlignment="0" attributes="0"> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + </DimensionLayout> + </Layout> + </Container> + <Component class="javax.swing.JButton" name="okButton"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.okButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/> + </Events> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="2" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JButton" name="cancelButton"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/> + </Events> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JTextField" name="licenseNumberTextField"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.licenseNumberTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="10" weightX="0.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + </SubComponents> +</Form> 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=<html>Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'</html>" + }) + 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") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//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(); + }// </editor-fold>//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<LicenseResponse> loadLicenseResponse() { + Optional<LicenseResponse> 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<LicenseInfo> 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 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<Form version="1.8" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> + <AuxValues> + <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/> + <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> + <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> + <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> + <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> + <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-109,0,0,1,-29"/> + </AuxValues> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> + <SubComponents> + <Container class="javax.swing.JPanel" name="licenseInfoPanel"> + <Properties> + <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> + <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo"> + <TitledBorder title="License Info"> + <ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </TitledBorder> + </Border> + </Property> + </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> + <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> + </AuxValues> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> + <SubComponents> + <Component class="javax.swing.JLabel" name="licenseInfoMessageLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="licenseInfoUserLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="licenseInfoExpiresLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="licenseInfoIdLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JButton" name="licenseInfoAddButton"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoAddButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="licenseInfoAddButtonActionPerformed"/> + </Events> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="12" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + </SubComponents> + </Container> + <Container class="javax.swing.JPanel" name="malwareScansPanel"> + <Properties> + <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> + <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo"> + <TitledBorder title="Malware Scans"> + <ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.malwareScansPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </TitledBorder> + </Border> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + + <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> + <SubComponents> + <Component class="javax.swing.JLabel" name="malwareScansMessageLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="maxHashLookupsLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="maxFileUploadsLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="countersResetLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.countersResetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="hashLookupsRemainingLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + <Component class="javax.swing.JLabel" name="fileUploadsRemainingLabel"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> + <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/> + </Constraint> + </Constraints> + </Component> + </SubComponents> + </Container> + </SubComponents> +</Form> 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<LicenseInfo> 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") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//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); + }// </editor-fold>//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=<html>User: {0}<br/>Email: {1}</html>", + "# {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<LicenseInfo, Void> { + + 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<AuthTokenResponse, Void> { + + 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 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 O7~0d-&t;ucLK6VGd8;V^ literal 0 HcmV?d00001 diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java new file mode 100644 index 0000000000..9adb7410ce --- /dev/null +++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/BatchProcessor.java @@ -0,0 +1,88 @@ +/** ************************************************************************* + ** 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 java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Consumer; + +/** + * Processes a batch when number of items reaches batchSize or flush. Processing + * blocks (and subsequently add and flush operations) until previous batch + * finishes. + */ +public class BatchProcessor<T> { + + private final ExecutorService processingExecutorService = Executors.newSingleThreadExecutor(); + + private final BlockingQueue<T> batchingQueue; + private final List<T> processingQueue; + private final int batchSize; + private final Consumer<List<T>> itemsConsumer; + private final long millisTimeout; + + private Future<?> lastProcessingFuture = CompletableFuture.runAsync(() -> { + }); + + public BatchProcessor(int batchSize, long millisTimeout, Consumer<List<T>> 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<String> 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<FileRecord> batchProcessor = new BatchProcessor<FileRecord>(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<LicenseInfo> 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<FileRecord> 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<String, List<Long>> md5ToObjId = new HashMap<>(); + List<String> 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<FileReputationResult> 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<Long> 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 @@ <!DOCTYPE ivy-module [ <!ENTITY javafx.version "17.0.7"> + <!ENTITY jackson.version "2.15.2"> ]> <ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra"> <info organisation="org.sleuthkit.autopsy" module="corelibs"/> @@ -87,7 +88,8 @@ <dependency conf="autopsy_core->default" org="net.htmlparser.jericho" name="jericho-html" rev="3.4"/> - <dependency conf="autopsy_core->default" org="com.fasterxml.jackson.dataformat" name="jackson-dataformat-csv" rev="2.15.2"/> + <dependency conf="autopsy_core->default" org="com.fasterxml.jackson.dataformat" name="jackson-dataformat-csv" rev="&jackson.version;"/> + <dependency conf="autopsy_core->default" org="com.fasterxml.jackson.datatype" name="jackson-datatype-jsr310" rev="&jackson.version;"/> <!-- better image resizing --> <dependency conf="autopsy_core->default" org="org.imgscalr" name="imgscalr-lib" rev="4.2" /> @@ -142,8 +144,8 @@ <override org="com.google.code.gson" module="gson" rev="2.9.0"/> <override org="com.google.guava" module="guava" rev="32.0.1-jre"/> - <override org="com.fasterxml.jackson.core" module="jackson-databind" rev="2.15.2"/> - <override org="com.fasterxml.jackson.core" module="jackson-core" rev="2.15.2"/> + <override org="com.fasterxml.jackson.core" module="jackson-databind" rev="&jackson.version;"/> + <override org="com.fasterxml.jackson.core" module="jackson-core" rev="&jackson.version;"/> <!-- changes to bouncy castle version may also be reflected in thirdparty/IcePDF 6.2.2 --> <override org="org.bouncycastle" module="bcprov-jdk15on" rev="1.70"/> 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 @@ <package>com.fasterxml.jackson.databind.type</package> <package>com.fasterxml.jackson.databind.util</package> <package>com.fasterxml.jackson.dataformat.csv</package> + <package>com.fasterxml.jackson.datatype.jsr310</package> + <package>com.fasterxml.jackson.datatype.jsr310.util</package> <package>com.github.lgooddatepicker.components</package> <package>com.github.lgooddatepicker.optionalusertools</package> <package>com.github.lgooddatepicker.zinternaltools</package> @@ -197,8 +199,6 @@ <package>com.google.rpc.context</package> <package>com.google.thirdparty.publicsuffix</package> <package>com.google.type</package> - <package>com.microsoft.schemas.vml</package> - <package>com.microsoft.schemas.vml.impl</package> <package>com.sun.javafx</package> <package>com.sun.javafx.animation</package> <package>com.sun.javafx.application</package> @@ -321,9 +321,6 @@ <package>com.twelvemonkeys.util.regex</package> <package>com.twelvemonkeys.util.service</package> <package>com.twelvemonkeys.xml</package> - <package>javax.annotation</package> - <package>javax.annotation.concurrent</package> - <package>javax.annotation.meta</package> <package>javafx.animation</package> <package>javafx.application</package> <package>javafx.beans</package> @@ -340,7 +337,6 @@ <package>javafx.event</package> <package>javafx.fxml</package> <package>javafx.geometry</package> - <package>javafx.graphics</package> <package>javafx.print</package> <package>javafx.scene</package> <package>javafx.scene.canvas</package> @@ -362,19 +358,9 @@ <package>javafx.stage</package> <package>javafx.util</package> <package>javafx.util.converter</package> - <package>javax.jms</package> - <package>javax.mail</package> - <package>javax.mail.event</package> - <package>javax.mail.internet</package> - <package>javax.mail.search</package> - <package>javax.mail.util</package> - <package>javax.servlet</package> - <package>javax.servlet.http</package> - <package>javax.xml.parsers</package> - <package>javax.xml.transform</package> - <package>javax.xml.transform.dom</package> - <package>javax.xml.transform.sax</package> - <package>javax.xml.transform.stream</package> + <package>javax.annotation</package> + <package>javax.annotation.concurrent</package> + <package>javax.annotation.meta</package> <package>jfxtras.animation</package> <package>jfxtras.css</package> <package>jfxtras.css.converters</package> @@ -442,16 +428,6 @@ <package>org.apache.commons.lang3.tuple</package> <package>org.apache.commons.logging</package> <package>org.apache.commons.logging.impl</package> - <package>org.apache.log</package> - <package>org.apache.log.filter</package> - <package>org.apache.log.format</package> - <package>org.apache.log.output</package> - <package>org.apache.log.output.db</package> - <package>org.apache.log.output.io</package> - <package>org.apache.log.output.io.rotate</package> - <package>org.apache.log.output.jms</package> - <package>org.apache.log.output.net</package> - <package>org.apache.log.util</package> <package>org.apache.commons.text</package> <package>org.apache.commons.validator.routines</package> <package>org.apache.commons.validator.routines.checkdigit</package> @@ -460,14 +436,7 @@ <package>org.apache.log4j.config</package> <package>org.apache.log4j.helpers</package> <package>org.apache.log4j.jdbc</package> - <package>org.apache.log4j.jmx</package> - <package>org.apache.log4j.lf5</package> - <package>org.apache.log4j.lf5.util</package> - <package>org.apache.log4j.lf5.viewer</package> - <package>org.apache.log4j.lf5.viewer.categoryexplorer</package> - <package>org.apache.log4j.lf5.viewer.configure</package> <package>org.apache.log4j.net</package> - <package>org.apache.log4j.nt</package> <package>org.apache.log4j.or</package> <package>org.apache.log4j.or.jms</package> <package>org.apache.log4j.or.sax</package> @@ -931,6 +900,10 @@ <runtime-relative-path>ext/jackson-dataformat-csv-2.15.2.jar</runtime-relative-path> <binary-origin>release/modules/ext/jackson-dataformat-csv-2.15.2.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/jackson-datatype-jsr310-2.15.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jackson-datatype-jsr310-2.15.2.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/javafx-base-17.0.7-linux.jar</runtime-relative-path> <binary-origin>release/modules/ext/javafx-base-17.0.7-linux.jar</binary-origin> 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 -- GitLab