Merge pull request #7828 from gdicristofaro/CT-7161-fileUpload

CT-7161 file upload
This commit is contained in:
Mark McKinnon 2023-08-15 19:52:38 -04:00 committed by GitHub
commit 56e7958ba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1778 additions and 895 deletions

View File

@ -27,7 +27,9 @@ import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.FileReputationRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.MetadataUploadRequest;
import com.basistech.df.cybertriage.autopsy.ctapi.util.CTHostIDGenerationUtil;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -45,6 +47,8 @@ public class CTApiDAO {
private static final String LICENSE_REQUEST_PATH = "/_ah/api/license/v1/activate";
private static final String AUTH_TOKEN_REQUEST_PATH = "/_ah/api/auth/v2/generate_token";
private static final String CTCLOUD_SERVER_HASH_PATH = "/_ah/api/reputation/v1/query/file/hash/md5?query_types=CORRELATION,MALWARE";
private static final String CTCLOUD_UPLOAD_FILE_METADATA_PATH = "/_ah/api/reputation/v1/upload/meta";
private static final String AUTOPSY_PRODUCT = "AUTOPSY";
private static final CTApiDAO instance = new CTApiDAO();
@ -74,15 +78,27 @@ public class CTApiDAO {
}
public AuthTokenResponse getAuthToken(DecryptedLicenseResponse decrypted) throws CTCloudException {
return getAuthToken(decrypted, false);
}
public AuthTokenResponse getAuthToken(DecryptedLicenseResponse decrypted, boolean fileUpload) throws CTCloudException {
AuthTokenRequest authTokenRequest = new AuthTokenRequest()
.setAutopsyVersion(getAppVersion())
.setRequestFileUpload(false)
.setRequestFileUpload(fileUpload)
.setBoostLicenseId(decrypted.getBoostLicenseId())
.setHostId(decrypted.getLicenseHostId());
return httpClient.doPost(AUTH_TOKEN_REQUEST_PATH, authTokenRequest, AuthTokenResponse.class);
}
public void uploadFile(String url, String fileName, InputStream fileIs) throws CTCloudException {
httpClient.doFileUploadPost(url, fileName, fileIs);
}
public void uploadMeta(AuthenticatedRequestData authenticatedRequestData, MetadataUploadRequest metaRequest) throws CTCloudException {
httpClient.doPost(CTCLOUD_UPLOAD_FILE_METADATA_PATH, getAuthParams(authenticatedRequestData), metaRequest, null);
}
private static Map<String, String> getAuthParams(AuthenticatedRequestData authenticatedRequestData) {
return new HashMap<String, String>() {
{

View File

@ -76,8 +76,7 @@ public class CTCloudException extends Exception{
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.",
return String.format("An API error %s occurred. Please try again, and contact Basis support at %s for help if the problem persists.",
StringUtils.isNotBlank(getCause().getLocalizedMessage()) ? "("+getCause().getLocalizedMessage()+")": "(Unknown)",
Constants.SUPPORT_AT_CYBERTRIAGE_DOT_COM );
}else {

View File

@ -21,42 +21,37 @@ package com.basistech.df.cybertriage.autopsy.ctapi;
import com.basistech.df.cybertriage.autopsy.ctapi.util.ObjectMapperUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.io.InputStream;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.logging.Level;
import java.util.stream.Stream;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
@ -64,102 +59,83 @@ import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
import org.apache.http.impl.client.WinHttpClients;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.ssl.SSLInitializationException;
import org.netbeans.core.ProxySettings;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coreutils.Version;
/**
* Makes the http requests to CT cloud.
*
* NOTE: regarding proxy settings, the host and port are handled by the
* NbProxySelector. Any proxy authentication is handled by NbAuthenticator which
* is installed at startup (i.e. NbAuthenticator.install). See
* GeneralOptionsModel.testHttpConnection to see how the general options panel
* tests the connection.
*/
public class CTCloudHttpClient {
class CTCloudHttpClient {
private static final CTCloudHttpClient instance = new CTCloudHttpClient();
private static final Logger LOGGER = Logger.getLogger(CTCloudHttpClient.class.getName());
private static final String HOST_URL = Version.getBuildType() == Version.Type.RELEASE ? Constants.CT_CLOUD_SERVER : Constants.CT_CLOUD_DEV_SERVER;
private static final List<String> DEFAULT_SCHEME_PRIORITY
= new ArrayList<>(Arrays.asList(
AuthSchemes.SPNEGO,
AuthSchemes.KERBEROS,
AuthSchemes.NTLM,
AuthSchemes.CREDSSP,
AuthSchemes.DIGEST,
AuthSchemes.BASIC));
private static final String NB_PROXY_SELECTOR_NAME = "org.netbeans.core.NbProxySelector";
private static final int CONNECTION_TIMEOUT_MS = 58 * 1000; // milli sec
private static final CTCloudHttpClient instance = new CTCloudHttpClient();
public static CTCloudHttpClient getInstance() {
return instance;
}
private final ObjectMapper mapper = ObjectMapperUtil.getInstance().getDefaultObjectMapper();
private final SSLContext sslContext;
private String hostName = null;
private final ProxySelector proxySelector;
private CTCloudHttpClient() {
// leave as null for now unless we want to customize this at a later date
this.sslContext = null;
this.sslContext = createSSLContext();
this.proxySelector = getProxySelector();
}
private ProxySettingArgs getProxySettings() {
if (StringUtils.isBlank(hostName)) {
try {
hostName = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException ex) {
LOGGER.log(Level.WARNING, "An error occurred while fetching the hostname", ex);
private static URI getUri(String host, String path, Map<String, String> urlReqParams) throws URISyntaxException {
String url = host + path;
URIBuilder builder = new URIBuilder(url);
if (!MapUtils.isEmpty(urlReqParams)) {
for (Entry<String, String> e : urlReqParams.entrySet()) {
String key = e.getKey();
String value = e.getValue();
if (StringUtils.isNotBlank(key) || StringUtils.isNotBlank(value)) {
builder.addParameter(key, value);
}
}
}
int proxyPort = 0;
if (StringUtils.isNotBlank(ProxySettings.getHttpPort())) {
try {
proxyPort = Integer.parseInt(ProxySettings.getHttpsPort());
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "Unable to convert port to integer");
}
}
return new ProxySettingArgs(
ProxySettings.getProxyType() != ProxySettings.DIRECT_CONNECTION,
hostName,
ProxySettings.getHttpsHost(),
proxyPort,
ProxySettings.getAuthenticationUsername(),
ProxySettings.getAuthenticationPassword(),
null
);
return builder.build();
}
public <O> O doPost(String urlPath, Object jsonBody, Class<O> classType) throws CTCloudException {
return doPost(urlPath, Collections.emptyMap(), jsonBody, classType);
}
public <O> O doPost(String urlPath, Map<String, String> urlReqParams, Object jsonBody, Class<O> classType) throws CTCloudException {
String url = HOST_URL + urlPath;
URI postURI = null;
try {
postURI = getUri(HOST_URL, urlPath, urlReqParams);
LOGGER.log(Level.INFO, "initiating http connection to ctcloud server");
try (CloseableHttpClient httpclient = createConnection(getProxySettings(), sslContext)) {
URIBuilder builder = new URIBuilder(url);
if (!MapUtils.isEmpty(urlReqParams)) {
for (Entry<String, String> e : urlReqParams.entrySet()) {
String key = e.getKey();
String value = e.getValue();
if (StringUtils.isNotBlank(key) || StringUtils.isNotBlank(value)) {
builder.addParameter(key, value);
}
}
}
try (CloseableHttpClient httpclient = createConnection(proxySelector, sslContext)) {
URI postURI = builder.build();
HttpPost postRequest = new HttpPost(postURI);
configureRequestTimeout(postRequest);
postRequest.setHeader("Content-type", "application/json");
@ -177,10 +153,14 @@ public class CTCloudHttpClient {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
LOGGER.log(Level.INFO, "Response Received. - Status OK");
// Parse Response
HttpEntity entity = response.getEntity();
String entityStr = EntityUtils.toString(entity);
O respObj = mapper.readValue(entityStr, classType);
return respObj;
if (classType != null) {
HttpEntity entity = response.getEntity();
String entityStr = EntityUtils.toString(entity);
O respObj = mapper.readValue(entityStr, classType);
return respObj;
} else {
return null;
}
} else {
LOGGER.log(Level.WARNING, "Response Received. - Status Error {}", response.getStatusLine());
handleNonOKResponse(response, "");
@ -191,16 +171,64 @@ public class CTCloudHttpClient {
}
}
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "IO Exception raised when connecting to CT Cloud using " + url, ex);
LOGGER.log(Level.WARNING, "IO Exception raised when connecting to CT Cloud using " + postURI, ex);
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex);
} catch (SSLInitializationException ex) {
LOGGER.log(Level.WARNING, "No such algorithm exception raised when creating SSL connection for CT Cloud using " + postURI, ex);
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex);
} catch (URISyntaxException ex) {
LOGGER.log(Level.WARNING, "Wrong URL syntax for CT Cloud " + url, ex);
LOGGER.log(Level.WARNING, "Wrong URL syntax for CT Cloud " + postURI, ex);
throw new CTCloudException(CTCloudException.ErrorCode.UNKNOWN, ex);
}
return null;
}
public void doFileUploadPost(String fullUrlPath, String fileName, InputStream fileIs) throws CTCloudException {
URI postUri;
try {
postUri = new URI(fullUrlPath);
} catch (URISyntaxException ex) {
LOGGER.log(Level.WARNING, "Wrong URL syntax for CT Cloud " + fullUrlPath, ex);
throw new CTCloudException(CTCloudException.ErrorCode.UNKNOWN, ex);
}
try (CloseableHttpClient httpclient = createConnection(proxySelector, sslContext)) {
LOGGER.log(Level.INFO, "initiating http post request to ctcloud server " + fullUrlPath);
HttpPost post = new HttpPost(postUri);
configureRequestTimeout(post);
post.addHeader("Connection", "keep-alive");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addBinaryBody(
"file",
fileIs,
ContentType.APPLICATION_OCTET_STREAM,
fileName
);
HttpEntity multipart = builder.build();
post.setEntity(multipart);
try (CloseableHttpResponse response = httpclient.execute(post)) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
LOGGER.log(Level.INFO, "Response Received. - Status OK");
} else {
LOGGER.log(Level.WARNING, MessageFormat.format("Response Received. - Status Error {0}", response.getStatusLine()));
handleNonOKResponse(response, fileName);
}
}
} catch (SSLInitializationException ex) {
LOGGER.log(Level.WARNING, "SSL exception raised when connecting to Reversing Labs for file content upload ", ex);
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex);
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "IO Exception raised when connecting to Reversing Labs for file content upload ", ex);
throw new CTCloudException(CTCloudException.ErrorCode.NETWORK_ERROR, ex);
}
}
/**
* A generic way to handle the HTTP response - when the response code is NOT
* 200 OK.
@ -263,147 +291,140 @@ public class CTCloudHttpClient {
}
/**
* Creates a connection to CT Cloud with the given arguments.
* @param proxySettings The network proxy settings.
* @param sslContext The ssl context or null.
* @return The connection to CT Cloud.
* Get ProxySelector present (favoring NbProxySelector if present).
*
* @return The found ProxySelector or null.
*/
private static CloseableHttpClient createConnection(ProxySettingArgs proxySettings, SSLContext sslContext) {
HttpClientBuilder builder = getHttpClientBuilder(proxySettings);
if (sslContext != null) {
builder.setSSLContext(sslContext);
}
return builder.build();
private static ProxySelector getProxySelector() {
Collection<? extends ProxySelector> selectors = Lookup.getDefault().lookupAll(ProxySelector.class);
return (selectors != null ? selectors.stream() : Stream.empty())
.filter(s -> s != null)
.map(s -> (ProxySelector) s)
.sorted((a, b) -> {
String aName = a.getClass().getCanonicalName();
String bName = b.getClass().getCanonicalName();
boolean aIsNb = aName.equalsIgnoreCase(NB_PROXY_SELECTOR_NAME);
boolean bIsNb = bName.equalsIgnoreCase(NB_PROXY_SELECTOR_NAME);
if (aIsNb == bIsNb) {
return StringUtils.compareIgnoreCase(aName, bName);
} else {
return aIsNb ? -1 : 1;
}
})
.findFirst()
// TODO take this out to remove proxy selector logging
.map(s -> new LoggingProxySelector(s))
.orElse(null);
}
private static HttpClientBuilder getHttpClientBuilder(ProxySettingArgs proxySettings) {
/**
* Create an SSLContext object using our in-memory keystore.
*
* @return
*/
private static SSLContext createSSLContext() {
LOGGER.log(Level.INFO, "Creating custom SSL context");
try {
if (proxySettings.isSystemOrManualProxy()) {
// I'm not sure how much of this is really necessary to set up, but it works
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
KeyManager[] keyManagers = getKeyManagers();
TrustManager[] trustManagers = getTrustManagers();
sslContext.init(keyManagers, trustManagers, new SecureRandom());
return sslContext;
} catch (NoSuchAlgorithmException | KeyManagementException ex) {
LOGGER.log(Level.SEVERE, "Error creating SSL context", ex);
return null;
}
}
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
LOGGER.info("Requesting Password Authentication...");
return super.getPasswordAuthentication();
}
});
// jvm default key manager
// based in part on this: https://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm/16229909
private static KeyManager[] getKeyManagers() {
LOGGER.log(Level.INFO, "Using default algorithm to create trust store: " + KeyManagerFactory.getDefaultAlgorithm());
try {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(null, null);
return kmf.getKeyManagers();
} catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException ex) {
LOGGER.log(Level.SEVERE, "Error getting KeyManagers", ex);
return new KeyManager[0];
}
HttpClientBuilder builder = null;
HttpHost proxyHost = null;
CredentialsProvider proxyCredsProvider = null;
RequestConfig config = null;
}
if (Objects.nonNull(proxySettings.getProxyHostname()) && proxySettings.getProxyPort() > 0) {
proxyHost = new HttpHost(proxySettings.getProxyHostname(), proxySettings.getProxyPort());
// jvm default trust store
// based in part on this: https://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm/16229909
private static TrustManager[] getTrustManagers() {
try {
LOGGER.log(Level.INFO, "Using default algorithm to create trust store: " + TrustManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf
= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
X509TrustManager tm = (X509TrustManager) tmf.getTrustManagers()[0];
proxyCredsProvider = getProxyCredentialsProvider(proxySettings);
if (StringUtils.isNotBlank(proxySettings.getAuthScheme())) {
if (!DEFAULT_SCHEME_PRIORITY.get(0).equalsIgnoreCase(proxySettings.getAuthScheme())) {
DEFAULT_SCHEME_PRIORITY.removeIf(s -> s.equalsIgnoreCase(proxySettings.getAuthScheme()));
DEFAULT_SCHEME_PRIORITY.add(0, proxySettings.getAuthScheme());
}
}
config = RequestConfig.custom().setProxyPreferredAuthSchemes(DEFAULT_SCHEME_PRIORITY).build();
}
if (Objects.isNull(proxyCredsProvider) && WinHttpClients.isWinAuthAvailable()) {
builder = WinHttpClients.custom();
builder.useSystemProperties();
LOGGER.log(Level.WARNING, "Using Win HTTP Client");
} else {
builder = HttpClients.custom();
builder.setDefaultRequestConfig(config);
if (Objects.nonNull(proxyCredsProvider)) { // make sure non null proxycreds before setting it
builder.setDefaultCredentialsProvider(proxyCredsProvider);
}
LOGGER.log(Level.WARNING, "Using default http client");
}
if (Objects.nonNull(proxyHost)) {
builder.setProxy(proxyHost);
LOGGER.log(Level.WARNING, MessageFormat.format("Using proxy {0}", proxyHost));
}
return builder;
} else {
return HttpClients.custom();
return new TrustManager[]{tm};
} catch (KeyStoreException | NoSuchAlgorithmException ex) {
LOGGER.log(Level.SEVERE, "Error getting TrustManager", ex);
return new TrustManager[0];
}
}
/**
* Returns a CredentialsProvider for proxy, if one is configured.
* Creates a connection to CT Cloud with the given arguments.
*
* @return CredentialsProvider, if a proxy is configured with credentials,
* null otherwise
* @param proxySelector The proxy selector.
* @param sslContext The ssl context or null.
* @return The connection to CT Cloud.
*/
private static CredentialsProvider getProxyCredentialsProvider(ProxySettingArgs proxySettings) {
CredentialsProvider proxyCredsProvider = null;
if (proxySettings.isSystemOrManualProxy()) {
if (StringUtils.isNotBlank(proxySettings.getProxyUserId())) {
if (null != proxySettings.getProxyPassword() && proxySettings.getProxyPassword().length > 0) { // Password will be blank for KERBEROS / NEGOTIATE schemes.
proxyCredsProvider = new SystemDefaultCredentialsProvider();
String userId = proxySettings.getProxyUserId();
String domain = null;
if (userId.contains("\\")) {
domain = userId.split("\\\\")[0];
userId = userId.split("\\\\")[1];
}
String workStation = proxySettings.getHostName();
proxyCredsProvider.setCredentials(new AuthScope(proxySettings.getProxyHostname(), proxySettings.getProxyPort()),
new NTCredentials(userId, new String(proxySettings.getProxyPassword()), workStation, domain));
}
}
private static CloseableHttpClient createConnection(ProxySelector proxySelector, SSLContext sslContext) throws SSLInitializationException {
HttpClientBuilder builder;
if (ProxySettings.getProxyType() != ProxySettings.DIRECT_CONNECTION
&& StringUtils.isBlank(ProxySettings.getAuthenticationUsername())
&& ArrayUtils.isEmpty(ProxySettings.getAuthenticationPassword())
&& WinHttpClients.isWinAuthAvailable()) {
builder = WinHttpClients.custom();
builder.useSystemProperties();
LOGGER.log(Level.WARNING, "Using Win HTTP Client");
} else {
builder = HttpClients.custom();
// builder.setDefaultRequestConfig(config);
LOGGER.log(Level.WARNING, "Using default http client");
}
return proxyCredsProvider;
if (sslContext != null) {
builder.setSSLContext(sslContext);
}
if (proxySelector != null) {
builder.setRoutePlanner(new SystemDefaultRoutePlanner(proxySelector));
}
return builder.build();
}
private static class ProxySettingArgs {
private static class LoggingProxySelector extends ProxySelector {
private final boolean systemOrManualProxy;
private final String hostName;
private final String proxyHostname;
private final int proxyPort;
private final String proxyUserId;
private final char[] proxyPassword;
private final String authScheme;
private final ProxySelector delegate;
ProxySettingArgs(boolean systemOrManualProxy, String hostName, String proxyHostname, int proxyPort, String proxyUserId, char[] proxyPassword, String authScheme) {
this.systemOrManualProxy = systemOrManualProxy;
this.hostName = hostName;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.proxyUserId = proxyUserId;
this.proxyPassword = proxyPassword;
this.authScheme = authScheme;
public LoggingProxySelector(ProxySelector delegate) {
this.delegate = delegate;
}
boolean isSystemOrManualProxy() {
return systemOrManualProxy;
@Override
public List<Proxy> select(URI uri) {
List<Proxy> selectedProxies = delegate.select(uri);
LOGGER.log(Level.INFO, MessageFormat.format("Proxy selected for {0} are {1}", uri, selectedProxies));
return selectedProxies;
}
String getHostName() {
return hostName;
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
LOGGER.log(Level.WARNING, MessageFormat.format("Connection failed connecting to {0} socket address {1}", uri, sa), ioe);
delegate.connectFailed(uri, sa, ioe);
}
String getProxyHostname() {
return proxyHostname;
}
int getProxyPort() {
return proxyPort;
}
String getProxyUserId() {
return proxyUserId;
}
char[] getProxyPassword() {
return proxyPassword;
}
public String getAuthScheme() {
return authScheme;
}
}
}

View File

@ -23,7 +23,7 @@ import java.net.URI;
/**
* Constants regarding connections to cyber triage cloud.
*/
final public class Constants {
final class Constants {
public static final String CYBER_TRIAGE = "CyberTriage";

View File

@ -1,446 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basistech.df.cybertriage.autopsy.ctapi;
import java.net.*;
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();
}
}

View File

@ -38,7 +38,7 @@ public class DecryptedLicenseResponse {
private final Long fileUploads;
private final Instant activationTime;
private final String product;
private final String limitType;
private final LicenseLimitType limitType;
private final String timezone;
private final String customerEmail;
private final String customerName;
@ -54,7 +54,7 @@ public class DecryptedLicenseResponse {
@JsonDeserialize(using = InstantEpochMillisDeserializer.class)
@JsonProperty("activationTime") Instant activationTime,
@JsonProperty("product") String product,
@JsonProperty("limitType") String limitType,
@JsonProperty("limitType") LicenseLimitType limitType,
@JsonProperty("timezone") String timezone,
@JsonProperty("customerEmail") String customerEmail,
@JsonProperty("customerName") String customerName
@ -96,7 +96,7 @@ public class DecryptedLicenseResponse {
return product;
}
public String getLimitType() {
public LicenseLimitType getLimitType() {
return limitType;
}

View File

@ -0,0 +1,30 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basistech.df.cybertriage.autopsy.ctapi.json;
/**
* The limit type (and reset) for the license.
*/
public enum LicenseLimitType {
HOURLY,
DAILY,
WEEKLY,
MONTHLY,
NO_RESET;
}

View File

@ -0,0 +1,109 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basistech.df.cybertriage.autopsy.ctapi.json;
import com.fasterxml.jackson.annotation.JsonProperty;
public class MetadataUploadRequest {
@JsonProperty("file_upload_url")
private String fileUploadUrl;
@JsonProperty("sha1")
private String sha1;
@JsonProperty("sha256")
private String sha256;
@JsonProperty("md5")
private String md5;
@JsonProperty("filePath")
private String filePath;
@JsonProperty("fileSize")
private Long fileSizeBytes;
@JsonProperty("createdDate")
private Long createdDate;
public String getFileUploadUrl() {
return fileUploadUrl;
}
public MetadataUploadRequest setFileUploadUrl(String fileUploadUrl) {
this.fileUploadUrl = fileUploadUrl;
return this;
}
public String getSha1() {
return sha1;
}
public MetadataUploadRequest setSha1(String sha1) {
this.sha1 = sha1;
return this;
}
public String getSha256() {
return sha256;
}
public MetadataUploadRequest setSha256(String sha256) {
this.sha256 = sha256;
return this;
}
public String getMd5() {
return md5;
}
public MetadataUploadRequest setMd5(String md5) {
this.md5 = md5;
return this;
}
public String getFilePath() {
return filePath;
}
public MetadataUploadRequest setFilePath(String filePath) {
this.filePath = filePath;
return this;
}
public Long getFileSizeBytes() {
return fileSizeBytes;
}
public MetadataUploadRequest setFileSizeBytes(Long fileSizeBytes) {
this.fileSizeBytes = fileSizeBytes;
return this;
}
public Long getCreatedDate() {
return createdDate;
}
public MetadataUploadRequest setCreatedDate(Long createdDate) {
this.createdDate = createdDate;
return this;
}
}

View File

@ -3,7 +3,7 @@
# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template
OptionsCategory_Name_CyberTriage=Cyber Triage
OptionsCategory_Keywords_CyberTriage=Cyber Triage,Cyber,Triage
LicenseDisclaimerPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use.</html>
LicenseDisclaimerPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from
LicenseDisclaimerPanel.link.text=<html><span style="color: blue; text-decoration: underline">https://cybertriage.com/autopsy-checkout</span></html>
LicenseDisclaimerPanel.border.title=Disclaimer
LicenseDisclaimerPanel.trialLabel.text=You can try a free 7-day trial from

View File

@ -3,7 +3,7 @@
# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template
OptionsCategory_Name_CyberTriage=Cyber Triage
OptionsCategory_Keywords_CyberTriage=Cyber Triage,Cyber,Triage
LicenseDisclaimerPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a non-free license to use.</html>
LicenseDisclaimerPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
LicenseDisclaimerPanel.purchaseFromLabel.text=You can purchase a license from
LicenseDisclaimerPanel.link.text=<html><span style="color: blue; text-decoration: underline">https://cybertriage.com/autopsy-checkout</span></html>
LicenseDisclaimerPanel.border.title=Disclaimer
LicenseDisclaimerPanel.trialLabel.text=You can try a free 7-day trial from

View File

@ -6,17 +6,18 @@
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Disclaimer">
<ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="LicenseDisclaimerPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<Color PropertyName="color" blue="0" green="0" red="ff" type="rgb"/>
</TitledBorder>
</Border>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2147483647, 90]"/>
<Dimension value="[2147483647, 108]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[562, 90]"/>
<Dimension value="[562, 108]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 90]"/>
<Dimension value="[400, 108]"/>
</Property>
</Properties>
<AuxValues>
@ -29,7 +30,7 @@
<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,90,0,0,2,50"/>
<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,108,0,0,2,50"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
@ -47,45 +48,7 @@
</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="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="purchaseFromLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="LicenseDisclaimerPanel.purchaseFromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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="-1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="3" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="link">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="LicenseDisclaimerPanel.link.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Hand Cursor"/>
</Property>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="linkMouseClicked"/>
</Events>
<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="-1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" 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>
@ -96,7 +59,7 @@
</AuxValues>
<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="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
<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="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
@ -113,5 +76,111 @@
</DimensionLayout>
</Layout>
</Container>
<Container class="javax.swing.JPanel" name="trialPanel">
<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="5" insetsBottom="5" insetsRight="5" anchor="10" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="trialLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="LicenseDisclaimerPanel.trialLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="trialLink">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="getHtmlLink(TRIAL_URL)" type="code"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Hand Cursor"/>
</Property>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="trialLinkMouseClicked"/>
</Events>
<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="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="purchasePanel">
<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="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="10" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="purchaseFromLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/Bundle.properties" key="LicenseDisclaimerPanel.purchaseFromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="purchaseLink">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="getHtmlLink(PURCHASE_URL)" type="code"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Hand Cursor"/>
</Property>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="purchaseLinkMouseClicked"/>
</Events>
<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="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -32,7 +32,12 @@ public class LicenseDisclaimerPanel extends javax.swing.JPanel {
private static final Logger LOGGER = Logger.getLogger(LicenseDisclaimerPanel.class.getName());
private static final String CHECKOUT_PAGE_URL = "https://cybertriage.com/autopsy-checkout";
private static final String TRIAL_URL = "https://cybertriage.com/autopsy-trial";
private static final String PURCHASE_URL = "https://cybertriage.com/autopsy-checkout";
private static String getHtmlLink(String url) {
return "<html><span style=\"color: blue; text-decoration: underline\">" + url + "</span></html>";
}
/**
* Creates new form LicenseDisclaimerPanel
@ -52,14 +57,18 @@ public class LicenseDisclaimerPanel extends javax.swing.JPanel {
java.awt.GridBagConstraints gridBagConstraints;
javax.swing.JLabel disclaimer = new javax.swing.JLabel();
javax.swing.JLabel purchaseFromLabel = new javax.swing.JLabel();
javax.swing.JLabel link = new javax.swing.JLabel();
javax.swing.JPanel spacer = new javax.swing.JPanel();
javax.swing.JPanel trialPanel = new javax.swing.JPanel();
javax.swing.JLabel trialLabel = new javax.swing.JLabel();
javax.swing.JLabel trialLink = new javax.swing.JLabel();
javax.swing.JPanel purchasePanel = new javax.swing.JPanel();
javax.swing.JLabel purchaseFromLabel = new javax.swing.JLabel();
javax.swing.JLabel purchaseLink = new javax.swing.JLabel();
setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.border.title"))); // NOI18N
setMaximumSize(new java.awt.Dimension(2147483647, 90));
setMinimumSize(new java.awt.Dimension(562, 90));
setPreferredSize(new java.awt.Dimension(400, 90));
setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI", 0, 12), new java.awt.Color(255, 0, 0))); // NOI18N
setMaximumSize(new java.awt.Dimension(2147483647, 108));
setMinimumSize(new java.awt.Dimension(562, 108));
setPreferredSize(new java.awt.Dimension(400, 108));
setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(disclaimer, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.disclaimer.text")); // NOI18N
@ -67,34 +76,12 @@ public class LicenseDisclaimerPanel extends javax.swing.JPanel {
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
add(disclaimer, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(purchaseFromLabel, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.purchaseFromLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 3);
add(purchaseFromLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(link, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.link.text")); // NOI18N
link.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
link.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
linkMouseClicked(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(link, gridBagConstraints);
javax.swing.GroupLayout spacerLayout = new javax.swing.GroupLayout(spacer);
spacer.setLayout(spacerLayout);
spacerLayout.setHorizontalGroup(
@ -108,24 +95,94 @@ public class LicenseDisclaimerPanel extends javax.swing.JPanel {
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridy = 3;
gridBagConstraints.weighty = 1.0;
add(spacer, gridBagConstraints);
trialPanel.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(trialLabel, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.trialLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
trialPanel.add(trialLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(trialLink, getHtmlLink(TRIAL_URL));
trialLink.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
trialLink.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
trialLinkMouseClicked(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
trialPanel.add(trialLink, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(trialPanel, gridBagConstraints);
purchasePanel.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(purchaseFromLabel, org.openide.util.NbBundle.getMessage(LicenseDisclaimerPanel.class, "LicenseDisclaimerPanel.purchaseFromLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
purchasePanel.add(purchaseFromLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(purchaseLink, getHtmlLink(PURCHASE_URL));
purchaseLink.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
purchaseLink.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
purchaseLinkMouseClicked(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
purchasePanel.add(purchaseLink, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
add(purchasePanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
private void linkMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_linkMouseClicked
private void purchaseLinkMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_purchaseLinkMouseClicked
gotoLink(PURCHASE_URL);
}//GEN-LAST:event_purchaseLinkMouseClicked
private void trialLinkMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_trialLinkMouseClicked
gotoLink(TRIAL_URL);
}//GEN-LAST:event_trialLinkMouseClicked
private void gotoLink(String url) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(new URI(CHECKOUT_PAGE_URL));
Desktop.getDesktop().browse(new URI(url));
} catch (IOException | URISyntaxException e) {
LOGGER.log(Level.SEVERE, "Error opening link to: " + CHECKOUT_PAGE_URL, e);
LOGGER.log(Level.SEVERE, "Error opening link to: " + url, e);
}
} else {
LOGGER.log(Level.WARNING, "Desktop API is not supported. Link cannot be opened.");
}
}//GEN-LAST:event_linkMouseClicked
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -24,3 +24,5 @@ CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text=
EULADialog.cancelButton.text=Cancel
EULADialog.acceptButton.text=Accept
EULADialog.title=Cyber Triage End User License Agreement
CTMalwareScannerOptionsPanel.fileUploadCheckbox.text=Upload file if hash lookup produces no results
CTMalwareScannerOptionsPanel.fileUploadPanel.border.title=File Upload

View File

@ -22,6 +22,10 @@ CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text=
CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text=
CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text=
CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text=
CTMalwareScannerOptionsPanel_getResetSuffix_daily=/day
CTMalwareScannerOptionsPanel_getResetSuffix_hourly=/hour
CTMalwareScannerOptionsPanel_getResetSuffix_monthly=/month
CTMalwareScannerOptionsPanel_getResetSuffix_weekly=/week
CTMalwareScannerOptionsPanel_licenseAddDialog_desc=License Number:
CTMalwareScannerOptionsPanel_licenseAddDialog_title=Add a License...
CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_desc=The license number has already been entered
@ -45,9 +49,11 @@ CTMalwareScannerOptionsPanel_malwareScans_fileUploadsRemaining=File uploads rema
# {0} - hashLookupsRemaining
CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining=Hash lookups remaining: {0}
# {0} - maxDailyFileLookups
CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}/day
# {1} - resetSuffix
CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}{1}
# {0} - maxDailyLookups
CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}/day
# {1} - resetSuffix
CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}{1}
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
@ -56,3 +62,5 @@ CTOPtionsPanel_loadMalwareScansInfo_loading=Loading...
EULADialog.cancelButton.text=Cancel
EULADialog.acceptButton.text=Accept
EULADialog.title=Cyber Triage End User License Agreement
CTMalwareScannerOptionsPanel.fileUploadCheckbox.text=Upload file if hash lookup produces no results
CTMalwareScannerOptionsPanel.fileUploadPanel.border.title=File Upload

View File

@ -27,7 +27,7 @@ import org.openide.util.NbBundle.Messages;
/**
* License dialog
*/
public class CTLicenseDialog extends javax.swing.JDialog {
class CTLicenseDialog extends javax.swing.JDialog {
private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[a-zA-Z0-9\\-]+?\\s*$");
private String licenseString = null;

View File

@ -26,7 +26,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.logging.Level;
@ -40,6 +39,7 @@ public class CTLicensePersistence {
private static final String CT_SETTINGS_DIR = "CyberTriage";
private static final String CT_LICENSE_FILENAME = "CyberTriageLicense.json";
private static final String MALWARE_INGEST_SETTINGS_FILENAME = "MalwareIngestSettings.json";
private static final Logger logger = Logger.getLogger(CTLicensePersistence.class.getName());
@ -91,7 +91,44 @@ public class CTLicensePersistence {
});
}
public synchronized boolean saveMalwareSettings(MalwareIngestSettings malwareIngestSettings) {
if (malwareIngestSettings != null) {
File settingsFile = getMalwareIngestFile();
try {
settingsFile.getParentFile().mkdirs();
objectMapper.writeValue(settingsFile, malwareIngestSettings);
return true;
} catch (IOException ex) {
logger.log(Level.WARNING, "There was an error writing malware ingest settings to file: " + settingsFile.getAbsolutePath(), ex);
}
}
return false;
}
public synchronized MalwareIngestSettings loadMalwareIngestSettings() {
MalwareIngestSettings settings = null;
File settingsFile = getMalwareIngestFile();
if (settingsFile.exists() && settingsFile.isFile()) {
try {
settings = objectMapper.readValue(settingsFile, MalwareIngestSettings.class);
} catch (IOException ex) {
logger.log(Level.WARNING, "There was an error reading malware ingest settings from file: " + settingsFile.getAbsolutePath(), ex);
}
}
if (settings == null) {
settings = new MalwareIngestSettings();
}
return settings;
}
private File getCTLicenseFile() {
return Paths.get(PlatformUtil.getModuleConfigDirectory(), CT_SETTINGS_DIR, CT_LICENSE_FILENAME).toFile();
}
private File getMalwareIngestFile() {
return Paths.get(PlatformUtil.getModuleConfigDirectory(), CT_SETTINGS_DIR, MALWARE_INGEST_SETTINGS_FILENAME).toFile();
}
}

View File

@ -11,11 +11,54 @@
<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"/>
<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,-69,0,0,1,-29"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="fileUploadPanel">
<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="File Upload">
<ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.fileUploadPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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="0" 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.JCheckBox" name="fileUploadCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<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.fileUploadCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileUploadCheckboxActionPerformed"/>
</Events>
<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="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="licenseInfoPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">

View File

@ -24,6 +24,7 @@ import com.basistech.df.cybertriage.autopsy.ctapi.CTApiDAO;
import com.basistech.df.cybertriage.autopsy.ctapi.json.AuthTokenResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.DecryptedLicenseResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseInfo;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseLimitType;
import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse;
import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil;
import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException;
@ -33,6 +34,7 @@ import java.io.IOException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -112,6 +114,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
@Override
public synchronized void saveSettings() {
ctPersistence.saveLicenseResponse(getLicenseInfo());
ctPersistence.saveMalwareSettings(getIngestSettings());
}
@Override
@ -128,12 +131,27 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
if (licenseInfo != null) {
loadMalwareScansInfo(licenseInfo);
}
MalwareIngestSettings ingestSettings = ctPersistence.loadMalwareIngestSettings();
setIngestSettings(ingestSettings);
}
private synchronized LicenseResponse getLicenseInfo() {
return this.licenseInfo == null ? null : this.licenseInfo.getLicenseResponse();
}
private MalwareIngestSettings getIngestSettings() {
return new MalwareIngestSettings()
.setUploadFiles(this.fileUploadCheckbox.isSelected());
}
private void setIngestSettings(MalwareIngestSettings ingestSettings) {
if (ingestSettings == null) {
ingestSettings = new MalwareIngestSettings();
}
this.fileUploadCheckbox.setSelected(ingestSettings.isUploadFiles());
}
private synchronized void setLicenseDisplay(LicenseInfo licenseInfo, String licenseMessage) {
this.licenseInfo = licenseInfo;
this.licenseInfoMessage = licenseMessage;
@ -202,6 +220,8 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
javax.swing.JPanel fileUploadPanel = new javax.swing.JPanel();
fileUploadCheckbox = new javax.swing.JCheckBox();
javax.swing.JPanel licenseInfoPanel = new javax.swing.JPanel();
licenseInfoMessageLabel = new javax.swing.JLabel();
licenseInfoUserLabel = new javax.swing.JLabel();
@ -218,6 +238,35 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
setLayout(new java.awt.GridBagLayout());
fileUploadPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.fileUploadPanel.border.title"))); // NOI18N
fileUploadPanel.setLayout(new java.awt.GridBagLayout());
fileUploadCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(fileUploadCheckbox, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.fileUploadCheckbox.text")); // NOI18N
fileUploadCheckbox.setMaximumSize(new java.awt.Dimension(32767, 20));
fileUploadCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
fileUploadCheckboxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
fileUploadPanel.add(fileUploadCheckbox, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
add(fileUploadPanel, gridBagConstraints);
licenseInfoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title"))); // NOI18N
licenseInfoPanel.setLayout(new java.awt.GridBagLayout());
@ -378,6 +427,10 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
}
}//GEN-LAST:event_licenseInfoAddButtonActionPerformed
private void fileUploadCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileUploadCheckboxActionPerformed
this.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_fileUploadCheckboxActionPerformed
@NbBundle.Messages({
"# {0} - userName",
"# {1} - email",
@ -387,9 +440,11 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
"# {0} - idNumber",
"CTMalwareScannerOptionsPanel_licenseInfo_id=ID: {0}",
"# {0} - maxDailyLookups",
"CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}/day",
"# {1} - resetSuffix",
"CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups=Max Hash lookups: {0}{1}",
"# {0} - maxDailyFileLookups",
"CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}/day",
"# {1} - resetSuffix",
"CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups=Max file uploads: {0}{1}",
"# {0} - countersResetDate",
"CTMalwareScannerOptionsPanel_malwareScans_countersReset=Counters reset: {0}",
"# {0} - hashLookupsRemaining",
@ -425,7 +480,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
this.malwareScansMessageLabel.setVisible(StringUtils.isNotBlank(this.authTokenMessage));
this.malwareScansMessageLabel.setText(this.authTokenMessage);
if (authTokenResponse == null) {
if (authTokenResponse == null || this.licenseInfo == null) {
this.maxHashLookupsLabel.setVisible(false);
this.maxFileUploadsLabel.setVisible(false);
this.countersResetLabel.setVisible(false);
@ -433,15 +488,62 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
this.fileUploadsRemainingLabel.setVisible(false);
} else {
this.maxHashLookupsLabel.setVisible(true);
this.maxHashLookupsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups(this.authTokenResponse.getHashLookupLimit()));
this.maxHashLookupsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyHashLookups(
this.authTokenResponse.getHashLookupLimit(),
getResetSuffix(this.licenseInfo.getDecryptedLicense().getLimitType())));
this.maxFileUploadsLabel.setVisible(true);
this.maxFileUploadsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups(this.authTokenResponse.getFileUploadLimit()));
this.maxFileUploadsLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_maxDailyFileLookups(
this.authTokenResponse.getFileUploadLimit(),
getResetSuffix(this.licenseInfo.getDecryptedLicense().getLimitType())));
this.countersResetLabel.setVisible(true);
this.countersResetLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(this.authTokenResponse.getResetDate() == null ? "" : MALWARE_SCANS_RESET_FORMAT.format(this.authTokenResponse.getResetDate())));
this.countersResetLabel.setText(getCountersResetText(this.licenseInfo.getDecryptedLicense().getLimitType(), this.authTokenResponse));
this.hashLookupsRemainingLabel.setVisible(true);
this.hashLookupsRemainingLabel.setText(Bundle.CTMalwareScannerOptionsPanel_malwareScans_hashLookupsRemaining(remaining(this.authTokenResponse.getHashLookupLimit(), this.authTokenResponse.getHashLookupCount())));
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())));
this.fileUploadsRemainingLabel.setText(
Bundle.CTMalwareScannerOptionsPanel_malwareScans_fileUploadsRemaining(
remaining(this.authTokenResponse.getFileUploadLimit(), this.authTokenResponse.getFileUploadCount())));
}
}
private static String getCountersResetText(LicenseLimitType limitType, AuthTokenResponse authTokenResponse) {
if (limitType == null || limitType == LicenseLimitType.NO_RESET) {
return "";
} else {
return Bundle.CTMalwareScannerOptionsPanel_malwareScans_countersReset(
MALWARE_SCANS_RESET_FORMAT.format(authTokenResponse.getResetDate()));
}
}
@Messages({
"CTMalwareScannerOptionsPanel_getResetSuffix_hourly=/hour",
"CTMalwareScannerOptionsPanel_getResetSuffix_daily=/day",
"CTMalwareScannerOptionsPanel_getResetSuffix_weekly=/week",
"CTMalwareScannerOptionsPanel_getResetSuffix_monthly=/month"
})
private String getResetSuffix(LicenseLimitType limitType) {
if (limitType == null) {
return "";
}
switch (limitType) {
case HOURLY:
return Bundle.CTMalwareScannerOptionsPanel_getResetSuffix_hourly();
case DAILY:
return Bundle.CTMalwareScannerOptionsPanel_getResetSuffix_daily();
case WEEKLY:
return Bundle.CTMalwareScannerOptionsPanel_getResetSuffix_weekly();
case MONTHLY:
return Bundle.CTMalwareScannerOptionsPanel_getResetSuffix_monthly();
case NO_RESET:
default:
return "";
}
}
@ -501,7 +603,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
try {
LicenseResponse licenseResponse = get();
SwingUtilities.invokeLater(() -> acceptEula(licenseResponse));
} catch (InterruptedException ex) {
} catch (InterruptedException | CancellationException ex) {
// ignore cancellation; just load current license
setLicenseDisplay(licenseInfo, null);
loadMalwareScansInfo(licenseInfo);
@ -557,7 +659,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
AuthTokenResponse authTokenResponse = null;
try {
authTokenResponse = get();
} catch (InterruptedException ex) {
} catch (InterruptedException | CancellationException ex) {
// ignore cancellation
} catch (ExecutionException ex) {
if (ex.getCause() != null && ex.getCause() instanceof CTCloudException cloudEx) {
@ -589,6 +691,7 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel countersResetLabel;
private javax.swing.JCheckBox fileUploadCheckbox;
private javax.swing.JLabel fileUploadsRemainingLabel;
private javax.swing.JLabel hashLookupsRemainingLabel;
private javax.swing.JButton licenseInfoAddButton;

View File

@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Dialog for displaying the Cyber Triage EULA before the license is saved.
*/
public class EULADialog extends javax.swing.JDialog {
class EULADialog extends javax.swing.JDialog {
private static final Logger LOGGER = Logger.getLogger(EULADialog.class.getName());
private static final String EULA_RESOURCE = "EULA.htm";

View File

@ -0,0 +1,37 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud;
/**
* Settings for the malware ingest module.
*/
public class MalwareIngestSettings {
private boolean uploadFiles = true;
public boolean isUploadFiles() {
return uploadFiles;
}
public MalwareIngestSettings setUploadFiles(boolean uploadFiles) {
this.uploadFiles = uploadFiles;
return this;
}
}

View File

@ -21,14 +21,10 @@ 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.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
@ -36,7 +32,7 @@ import java.util.function.Consumer;
* blocks (and subsequently add and flush operations) until previous batch
* finishes.
*/
public class BatchProcessor<T> {
class BatchProcessor<T> {
private ExecutorService processingExecutorService = Executors.newSingleThreadExecutor();

View File

@ -1,3 +1,7 @@
MalwareScanIngestModule_longPollForNotFound_fileLookupPolling_desc=Waiting for all uploaded files to complete scanning.
MalwareScanIngestModule_longPollForNotFound_fileLookupPolling_title=Waiting for File Upload Results
MalwareScanIngestModule_longPollForNotFound_timeout_desc=There was a timeout while waiting for file uploads to be processed. Please try again later.
MalwareScanIngestModule_longPollForNotFound_timeout_title=File Upload Results Timeout
MalwareScanIngestModule_malwareTypeDisplayName=Malware
# {0} - errorResponse
MalwareScanIngestModule_SharedProcessing_authTokenResponseError_desc=Received error: ''{0}'' when fetching the API authentication token for the license
@ -6,6 +10,8 @@ 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_exhaustedResultsHashLookups_desc=Not all files were processed because hash lookup limits were exceeded. Please try again when your limits reset.
MalwareScanIngestModule_SharedProcessing_exhaustedResultsHashLookups_title=Lookup Limits Exceeded
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
@ -16,12 +22,22 @@ MalwareScanIngestModule_SharedProcessing_repServicenResponseError_title=Lookup A
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
MalwareScanIngestModule_ShareProcessing_lowLookupsLimitWarning_desc=This license only has {0} lookups remaining.
MalwareScanIngestModule_ShareProcessing_lowLookupsLimitWarning_title=Hash Lookups Low
# {0} - remainingUploads
MalwareScanIngestModule_ShareProcessing_lowUploadsLimitWarning_desc=This license only has {0} file uploads remaining.
MalwareScanIngestModule_ShareProcessing_lowUploadsLimitWarning_title=File Uploads Limit Low
MalwareScanIngestModule_ShareProcessing_noLicense_desc=No Cyber Triage license could be loaded. Cyber Triage processing will be disabled.
MalwareScanIngestModule_ShareProcessing_noLicense_title=No Cyber Triage License
MalwareScanIngestModule_ShareProcessing_noRemaining_desc=There are no more remaining hash lookups for this license at this time. Cyber Triage processing will be disabled.
MalwareScanIngestModule_ShareProcessing_noRemaining_title=No remaining lookups
MalwareScanIngestModule_ShareProcessing_noLookupsRemaining_desc=There are no more remaining hash lookups for this license at this time. Malware scanning will be disabled.
MalwareScanIngestModule_ShareProcessing_noLookupsRemaining_title=No remaining lookups
MalwareScanIngestModule_ShareProcessing_noUploadsRemaining_desc=There are no more remaining file uploads for this license at this time. File uploading will be disabled.
MalwareScanIngestModule_ShareProcessing_noUploadsRemaining_title=No remaining file uploads
MalwareScanIngestModule_uploadFile_noRemainingFileUploads_desc=There are no more file uploads on this license at this time. File uploads will be disabled for remaining uploads.
MalwareScanIngestModule_uploadFile_noRemainingFileUploads_title=No Remaining File Uploads
# {0} - objectId
MalwareScanIngestModule_uploadFile_notUploadable_desc=A file did not meet requirements for upload (object id: {0}).
MalwareScanIngestModule_uploadFile_notUploadable_title=Not Able to Upload
MalwareScanIngestModuleFactory_description=The malware scan ingest module queries the Cyber Triage cloud API for any possible malicious executables.
MalwareScanIngestModuleFactory_displayName=Cyber Triage Malware Scanner
MalwareScanIngestModuleFactory_version=1.0.0

View File

@ -0,0 +1,201 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basistech.df.cybertriage.autopsy.malwarescan;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utility class to anonymize paths.
*/
class PathNormalizer {
private static final Logger LOGGER = Logger.getLogger(PathNormalizer.class.getName());
private static final String ANONYMIZED_USERNAME = "<user>";
private static final String ANONYMIZED_IP = "<private_ip>";
private static final String ANONYMIZED_HOSTNAME = "<hostname>";
private static final String FORWARD_SLASH = "/";
private static final String BACK_SLASH = "\\";
private static final Pattern USER_PATH_FORWARD_SLASH_REGEX = Pattern.compile("(?<!all )([/]{0,1}\\Qusers\\E/)(?!(public|Default|defaultAccount|All Users))([^/]+)(/){0,1}", Pattern.CASE_INSENSITIVE);
private static final Pattern USER_PATH_BACK_SLASH_REGEX = Pattern.compile("(?<!all )([\\\\]{0,1}\\Qusers\\E\\\\)(?!(public|Default|defaultAccount|All Users))([^\\\\]+)([\\\\]){0,1}", Pattern.CASE_INSENSITIVE);
private static final Pattern USER_PATH_FORWARD_SLASH_REGEX_XP = Pattern.compile("([/]{0,1}\\Qdocuments and settings\\E/)(?!(Default User|All Users))([^/]+)(/){0,1}", Pattern.CASE_INSENSITIVE);
private static final Pattern USER_PATH_BACK_SLASH_REGEX_XP = Pattern.compile("([\\\\]{0,1}\\Qdocuments and settings\\E\\\\)(?!(Default User|All Users))([^\\\\]+)(\\\\){0,1}", Pattern.CASE_INSENSITIVE);
private static final Pattern UNC_PATH_FORWARD_SLASH_PATTERN = Pattern.compile("(//)([^/]+)(/){0,1}");
private static final Pattern UNC_PATH_BACK_SLASH_PATTERN = Pattern.compile("(\\\\\\\\)([^\\\\]+)(\\\\){0,1}");
private static final String USERNAME_REGEX_REPLACEMENT = "$1" + ANONYMIZED_USERNAME + "$4";
private final SleuthkitCase skCase;
PathNormalizer(SleuthkitCase skCase) {
this.skCase = skCase;
}
protected List<String> getUsernames() {
try {
return this.skCase.getOsAccountManager().getOsAccounts().stream()
.filter(acct -> acct != null)
.map(acct -> acct.getLoginName().orElse(null))
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "There was an error getting current os accounts", ex);
return Collections.emptyList();
}
}
public String normalizePath(String inputString) {
if (StringUtils.isBlank(inputString)) {
return "";
}
String anonymousString = anonymizeUserFromPathsWithForwardSlashes(inputString);
anonymousString = anonymizeUserFromPathsWithBackSlashes(anonymousString);
anonymousString = anonymizeServerFromUNCPath(anonymousString);
return anonymousString;
}
private String anonymizeUserFromPathsWithForwardSlashes(String stringWithUsername) {
String anonymousString = stringWithUsername;
anonymousString = regexReplace(anonymousString, USER_PATH_FORWARD_SLASH_REGEX_XP, USERNAME_REGEX_REPLACEMENT);
anonymousString = regexReplace(anonymousString, USER_PATH_FORWARD_SLASH_REGEX, USERNAME_REGEX_REPLACEMENT);
anonymousString = replaceFolder(anonymousString, getUsernames(), ANONYMIZED_USERNAME, FORWARD_SLASH);
return anonymousString;
}
// Most paths in CyberTriage are normalized with forward slashes
// but there can still be strings containing paths that are not normalized such paths contained in arguments or event log payloads
private String anonymizeUserFromPathsWithBackSlashes(String stringWithUsername) {
String anonymousString = stringWithUsername;
anonymousString = regexReplace(anonymousString, USER_PATH_BACK_SLASH_REGEX_XP, USERNAME_REGEX_REPLACEMENT);
anonymousString = regexReplace(anonymousString, USER_PATH_BACK_SLASH_REGEX, USERNAME_REGEX_REPLACEMENT);
anonymousString = replaceFolder(anonymousString, getUsernames(), ANONYMIZED_USERNAME, BACK_SLASH);
return anonymousString;
}
private String anonymizeServerFromUNCPath(String inputString) {
Set<String> serverNames = new HashSet<>();
String anonymousString = inputString.toLowerCase(Locale.ENGLISH);
Matcher forwardSlashMatcher = UNC_PATH_FORWARD_SLASH_PATTERN.matcher(anonymousString);
while (forwardSlashMatcher.find()) {
String serverName = forwardSlashMatcher.group(2);
serverNames.add(serverName);
}
Matcher backSlashMatcher = UNC_PATH_BACK_SLASH_PATTERN.matcher(anonymousString);
while (backSlashMatcher.find()) {
String serverName = backSlashMatcher.group(2);
serverNames.add(serverName);
}
for (String serverName : serverNames) {
if (StringUtils.isBlank(serverName)) {
continue;
}
if (InetAddresses.isInetAddress(serverName) && isLocalIP(serverName)) {
anonymousString = replaceFolder(anonymousString, Collections.singletonList(serverName), ANONYMIZED_IP);
} else {
anonymousString = replaceFolder(anonymousString, Collections.singletonList(serverName), ANONYMIZED_HOSTNAME);
}
}
return anonymousString;
}
private static String regexReplace(String orig, Pattern pattern, String regexReplacement) {
Matcher matcher = pattern.matcher(orig);
return matcher.replaceAll(regexReplacement);
}
private static String replaceFolder(String orig, List<String> valuesToReplace, String replacementValue) {
String anonymized = orig;
anonymized = replaceFolder(anonymized, valuesToReplace, replacementValue, FORWARD_SLASH);
anonymized = replaceFolder(anonymized, valuesToReplace, replacementValue, BACK_SLASH);
return anonymized;
}
private static String replaceFolder(String orig, List<String> valuesToReplace, String replacementValue, String folderDelimiter) {
if (orig == null || valuesToReplace == null) {
return orig;
}
String anonymousString = orig;
// ensure non-null
folderDelimiter = StringUtils.defaultString(folderDelimiter);
replacementValue = StringUtils.defaultString(replacementValue);
// replace
for (String valueToReplace : valuesToReplace) {
if (StringUtils.isNotEmpty(valueToReplace)) {
anonymousString = StringUtils.replace(anonymousString,
folderDelimiter + valueToReplace + folderDelimiter,
folderDelimiter + replacementValue + folderDelimiter);
}
}
return anonymousString;
}
/**
* Returns true if IP Address is Any Local / Site Local / Link Local / Loop
* back local. Sample list "0.0.0.0", wildcard addres
* "10.1.1.1","10.10.10.10", site local address "127.0.0.0","127.2.2.2",
* loopback address "169.254.0.0","169.254.10.10", Link local address
* "172.16.0.0","172.31.245.245", site local address
*
* @param ipAddress
* @return
*/
public static boolean isLocalIP(String ipAddress) {
try {
InetAddress a = InetAddresses.forString(ipAddress);
return a.isAnyLocalAddress() || a.isSiteLocalAddress()
|| a.isLoopbackAddress() || a.isLinkLocalAddress();
} catch (IllegalArgumentException ex) {
LOGGER.log(Level.WARNING, "Invalid IP string", ex);
return false;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB