mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-29 22:09:52 +00:00
merge in refactoring of top programs
This commit is contained in:
@@ -173,7 +173,7 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements
|
||||
throw new TskCoreException("Error finding 7-Zip executable"); // NON-NLS
|
||||
}
|
||||
|
||||
String outputFolderSwitch = "-o" + String.format("\"%s\"",outputFolder); // NON-NLS
|
||||
String outputFolderSwitch = String.format("\"-o%s\"",outputFolder); // NON-NLS
|
||||
ProcessBuilder procBuilder = new ProcessBuilder();
|
||||
procBuilder.command(
|
||||
String.format("\"%s\"",sevenZipExe.getAbsolutePath()),
|
||||
|
||||
@@ -119,6 +119,8 @@ public final class IconsUtil {
|
||||
imageFile = "validationFailed.png"; //NON-NLS
|
||||
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE.getTypeID()) {
|
||||
imageFile = "web-account-type.png"; //NON-NLS
|
||||
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
|
||||
imageFile = "web-form-address.png"; //NON-NLS
|
||||
} else {
|
||||
imageFile = "artifact-icon.png"; //NON-NLS
|
||||
}
|
||||
|
||||
+14
@@ -398,6 +398,20 @@ final class DataSourceInfoUtilities {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the int value of a certain attribute type from an artifact.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @param attributeType The attribute type.
|
||||
*
|
||||
* @return The 'getValueInt()' value or null if the attribute could not be
|
||||
* retrieved.
|
||||
*/
|
||||
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the long value of a certain attribute type from an artifact and
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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 org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||
import java.io.File;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Provides information to populate Top Programs Summary queries.
|
||||
*/
|
||||
public class TopProgramsSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
||||
));
|
||||
|
||||
/**
|
||||
* A SQL join type.
|
||||
*/
|
||||
private enum JoinType {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
INNER,
|
||||
OUTER
|
||||
}
|
||||
|
||||
/**
|
||||
* A blackboard attribute value column.
|
||||
*/
|
||||
private enum AttributeColumn {
|
||||
value_text,
|
||||
value_int32,
|
||||
value_int64
|
||||
}
|
||||
|
||||
/**
|
||||
* The suffix joined to a key name for use as an identifier of a query.
|
||||
*/
|
||||
private static final String QUERY_SUFFIX = "_query";
|
||||
|
||||
/**
|
||||
* Functions that determine the folder name of a list of path elements. If
|
||||
* not matched, function returns null.
|
||||
*/
|
||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||
(pathList) -> {
|
||||
if (pathList.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String rootParent = pathList.get(0).toUpperCase();
|
||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||
return pathList.get(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||
(pathList) -> {
|
||||
for (String pathEl : pathList) {
|
||||
String uppered = pathEl.toUpperCase();
|
||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||
return "AppData";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a sql statement querying the blackboard attributes table for a
|
||||
* particular attribute type and returning a specified value. That query
|
||||
* also joins with the blackboard artifact table.
|
||||
*
|
||||
* @param joinType The type of join statement to create.
|
||||
* @param attributeColumn The blackboard attribute column that should be
|
||||
* returned.
|
||||
* @param attrType The attribute type to query for.
|
||||
* @param keyName The aliased name of the attribute to return. This
|
||||
* is also used to calculate the alias of the query
|
||||
* same as getFullKey.
|
||||
* @param bbaName The blackboard artifact table alias.
|
||||
*
|
||||
* @return The generated sql statement.
|
||||
*/
|
||||
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
|
||||
String queryName = keyName + QUERY_SUFFIX;
|
||||
String innerQueryName = "inner_attribute_" + queryName;
|
||||
|
||||
return "\n" + joinType + " JOIN (\n"
|
||||
+ " SELECT \n"
|
||||
+ " " + innerQueryName + ".artifact_id,\n"
|
||||
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
|
||||
+ " FROM blackboard_attributes " + innerQueryName + "\n"
|
||||
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
|
||||
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a column key, creates the full name for the column key.
|
||||
*
|
||||
* @param key The column key.
|
||||
*
|
||||
* @return The full identifier for the column key.
|
||||
*/
|
||||
private static String getFullKey(String key) {
|
||||
return key + QUERY_SUFFIX + "." + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SQL 'where' statement from a list of clauses and puts
|
||||
* parenthesis around each clause.
|
||||
*
|
||||
* @param clauses The clauses
|
||||
*
|
||||
* @return The generated 'where' statement.
|
||||
*/
|
||||
private static String getWhereString(List<String> clauses) {
|
||||
if (clauses.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> parenthesized = clauses.stream()
|
||||
.map(c -> "(" + c + ")")
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a [column] LIKE sql clause.
|
||||
*
|
||||
* @param column The column identifier.
|
||||
* @param likeString The string that will be used as column comparison.
|
||||
* @param isLike if false, the statement becomes NOT LIKE.
|
||||
*
|
||||
* @return The generated statement.
|
||||
*/
|
||||
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
||||
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
||||
}
|
||||
|
||||
private final SleuthkitCaseProvider provider;
|
||||
|
||||
public TopProgramsSummary() {
|
||||
this(SleuthkitCaseProvider.DEFAULT);
|
||||
}
|
||||
|
||||
public TopProgramsSummary(SleuthkitCaseProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getArtifactTypeIdsForRefresh() {
|
||||
return ARTIFACT_UPDATE_TYPE_IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of the top programs used on the data source. Currently
|
||||
* determines this based off of which prefetch results return the highest
|
||||
* count.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The number of programs to return.
|
||||
*
|
||||
* @return The top results objects found.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count)
|
||||
throws SleuthkitCaseProviderException, TskCoreException, SQLException {
|
||||
if (dataSource == null || count <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// ntosboot should be ignored
|
||||
final String ntosBootIdentifier = "NTOSBOOT";
|
||||
// programs in windows directory to be ignored
|
||||
final String windowsDir = "/WINDOWS%";
|
||||
|
||||
final String nameParam = "name";
|
||||
final String pathParam = "path";
|
||||
final String runCountParam = "run_count";
|
||||
final String lastRunParam = "last_run";
|
||||
|
||||
String bbaQuery = "bba";
|
||||
|
||||
final String query = "SELECT\n"
|
||||
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
|
||||
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
|
||||
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
|
||||
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
|
||||
+ getWhereString(Arrays.asList(
|
||||
bbaQuery + ".artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
|
||||
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
|
||||
// exclude ntosBootIdentifier from results
|
||||
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
|
||||
// exclude windows directory items from results
|
||||
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
|
||||
))
|
||||
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
|
||||
+ "ORDER BY \n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
|
||||
+ " " + getFullKey(nameParam) + " ASC";
|
||||
|
||||
DataSourceInfoUtilities.ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
|
||||
List<TopProgramsResult> progResults = new ArrayList<>();
|
||||
|
||||
boolean quitAtCount = false;
|
||||
|
||||
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
|
||||
long lastRunEpoch = resultSet.getLong(lastRunParam);
|
||||
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
|
||||
|
||||
Long runCount = resultSet.getLong(runCountParam);
|
||||
if (resultSet.wasNull()) {
|
||||
runCount = null;
|
||||
}
|
||||
|
||||
if (lastRun != null || runCount != null) {
|
||||
quitAtCount = true;
|
||||
}
|
||||
|
||||
progResults.add(new TopProgramsResult(
|
||||
resultSet.getString(nameParam),
|
||||
resultSet.getString(pathParam),
|
||||
runCount,
|
||||
lastRun));
|
||||
}
|
||||
|
||||
return progResults;
|
||||
};
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query);
|
||||
ResultSet resultSet = dbQuery.getResultSet()) {
|
||||
|
||||
return handler.process(resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
* @param applicationName The application name.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
public String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||
|
||||
File file = new File(strPath);
|
||||
while (file != null && StringUtils.isNotBlank(file.getName())) {
|
||||
pathEls.add(file.getName());
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
Collections.reverse(pathEls);
|
||||
|
||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||
String result = matchEntry.apply(pathEls);
|
||||
if (StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a result of a program run on a datasource.
|
||||
*/
|
||||
public static class TopProgramsResult {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
private final Long runTimes;
|
||||
private final Date lastRun;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
this.runTimes = runTimes;
|
||||
this.lastRun = lastRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the program
|
||||
*/
|
||||
public String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
public String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of run times or null if not present.
|
||||
*/
|
||||
public Long getRunTimes() {
|
||||
return runTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last time the program was run or null if not present.
|
||||
*/
|
||||
public Date getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
+302
-1
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||
|
||||
import java.io.File;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -30,6 +31,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -54,6 +56,36 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
*/
|
||||
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* Functions that determine the folder name of a list of path elements. If
|
||||
* not matched, function returns null.
|
||||
*/
|
||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||
(pathList) -> {
|
||||
if (pathList.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String rootParent = pathList.get(0).toUpperCase();
|
||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||
return pathList.get(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||
(pathList) -> {
|
||||
for (String pathEl : pathList) {
|
||||
String uppered = pathEl.toUpperCase();
|
||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||
return "AppData";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
|
||||
private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
||||
|
||||
@@ -69,17 +101,51 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
|
||||
private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
|
||||
private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
|
||||
private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
|
||||
private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
|
||||
private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
|
||||
|
||||
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
|
||||
private static final String WINDOWS_PREFIX = "/WINDOWS";
|
||||
|
||||
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
|
||||
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
|
||||
|
||||
/**
|
||||
* Sorts TopProgramsResults pushing highest run time count then most recent
|
||||
* run and then the program name that comes earliest in the alphabet.
|
||||
*/
|
||||
private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
|
||||
// first priority for sorting is the run times
|
||||
// if non-0, this is the return value for the comparator
|
||||
int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
|
||||
if (runTimesCompare != 0) {
|
||||
return -runTimesCompare;
|
||||
}
|
||||
|
||||
// second priority for sorting is the last run date
|
||||
// if non-0, this is the return value for the comparator
|
||||
int lastRunCompare = nullableCompare(
|
||||
a.getLastRun() == null ? null : a.getLastRun().getTime(),
|
||||
b.getLastRun() == null ? null : b.getLastRun().getTime());
|
||||
|
||||
if (lastRunCompare != 0) {
|
||||
return -lastRunCompare;
|
||||
}
|
||||
|
||||
// otherwise sort alphabetically
|
||||
return (a.getProgramName() == null ? "" : a.getProgramName())
|
||||
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
|
||||
};
|
||||
|
||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
|
||||
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
||||
));
|
||||
|
||||
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
|
||||
@@ -539,6 +605,188 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
* @param applicationName The application name.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
public String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||
|
||||
File file = new File(strPath);
|
||||
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
|
||||
pathEls.add(file.getName());
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
Collections.reverse(pathEls);
|
||||
|
||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||
String result = matchEntry.apply(pathEls);
|
||||
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact.
|
||||
*
|
||||
* @param artifact The TSK_PROG_RUN blackboard artifact.
|
||||
*
|
||||
* @return The generated TopProgramsResult.
|
||||
*/
|
||||
private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
|
||||
String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
|
||||
String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
|
||||
|
||||
// ignore items with no name or a ntos boot identifier
|
||||
if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ignore windows directory
|
||||
if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
|
||||
Long longCount = (count == null) ? null : (long) count;
|
||||
|
||||
return new TopProgramsResult(
|
||||
programName,
|
||||
path,
|
||||
longCount,
|
||||
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the maximum date given two (possibly null) dates.
|
||||
*
|
||||
* @param date1 First date.
|
||||
* @param date2 Second date.
|
||||
*
|
||||
* @return The maximum non-null date or null if both items are null.
|
||||
*/
|
||||
private static Date getMax(Date date1, Date date2) {
|
||||
if (date1 == null) {
|
||||
return date2;
|
||||
} else if (date2 == null) {
|
||||
return date1;
|
||||
} else {
|
||||
return date1.compareTo(date2) > 0 ? date1 : date2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the compare value favoring the higher non-null number.
|
||||
*
|
||||
* @param long1 First possibly null long.
|
||||
* @param long2 Second possibly null long.
|
||||
*
|
||||
* @return Returns the compare value: 1,0,-1 favoring the higher non-null
|
||||
* value.
|
||||
*/
|
||||
private static int nullableCompare(Long long1, Long long2) {
|
||||
if (long1 == null && long2 == null) {
|
||||
return 0;
|
||||
} else if (long1 != null && long2 == null) {
|
||||
return 1;
|
||||
} else if (long1 == null && long2 != null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Long.compare(long1, long2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if number is non-null and higher than 0.
|
||||
*
|
||||
* @param longNum The number.
|
||||
*
|
||||
* @return True if non-null and higher than 0.
|
||||
*/
|
||||
private static boolean isPositiveNum(Long longNum) {
|
||||
return longNum != null && longNum > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the top programs results for the given data source limited to
|
||||
* the count provided as a parameter. The highest run times are at the top
|
||||
* of the list. If that information isn't available the last run date is
|
||||
* used. If both, the last run date and the number of run times are
|
||||
* unavailable, the programs will be sorted alphabetically, the count will
|
||||
* be ignored and all items will be returned.
|
||||
*
|
||||
* @param dataSource The datasource. If the datasource is null, an empty
|
||||
* list will be returned.
|
||||
* @param count The number of results to return. This value must be > 0
|
||||
* or an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @return The sorted list and limited to the count if last run or run count
|
||||
* information is available on any item.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
|
||||
assertValidCount(count);
|
||||
|
||||
if (dataSource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Get TopProgramsResults for each TSK_PROG_RUN artifact
|
||||
Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
|
||||
.stream()
|
||||
// convert to a TopProgramsResult object or null if missing critical information
|
||||
.map((art) -> getTopProgramsResult(art))
|
||||
// remove any null items
|
||||
.filter((res) -> res != null)
|
||||
// group by the program name and program path
|
||||
// The value will be a TopProgramsResult with the max run times
|
||||
// and most recent last run date for each program name / program path pair.
|
||||
.collect(Collectors.toMap(
|
||||
res -> Pair.of(res.getProgramName(), res.getProgramPath()),
|
||||
res -> res,
|
||||
(res1, res2) -> {
|
||||
return new TopProgramsResult(
|
||||
res1.getProgramName(),
|
||||
res1.getProgramPath(),
|
||||
getMax(res1.getRunTimes(), res2.getRunTimes()),
|
||||
getMax(res1.getLastRun(), res2.getLastRun()));
|
||||
})).values();
|
||||
|
||||
List<TopProgramsResult> orderedResults = results.stream()
|
||||
.sorted(TOP_PROGRAMS_RESULT_COMPARE)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// only limit the list to count if there is no last run date and no run times.
|
||||
if (orderedResults.size() > 0) {
|
||||
TopProgramsResult topResult = orderedResults.get(0);
|
||||
// if run times / last run information is available, the first item should have some value,
|
||||
// and then the items should be limited accordingly.
|
||||
if (isPositiveNum(topResult.getRunTimes())
|
||||
|| (topResult.getLastRun() != null && isPositiveNum(topResult.getLastRun().getTime()))) {
|
||||
return orderedResults.stream().limit(count).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise return the alphabetized list with no limit applied.
|
||||
return orderedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object containing information about a web search artifact.
|
||||
*/
|
||||
@@ -722,4 +970,57 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
return lastVisit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a result of a program run on a datasource.
|
||||
*/
|
||||
public static class TopProgramsResult {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
private final Long runTimes;
|
||||
private final Date lastRun;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
this.runTimes = runTimes;
|
||||
this.lastRun = lastRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the program
|
||||
*/
|
||||
public String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
public String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of run times or null if not present.
|
||||
*/
|
||||
public Long getRunTimes() {
|
||||
return runTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last time the program was run or null if not present.
|
||||
*/
|
||||
public Date getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,11 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
@@ -227,35 +226,30 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
private final TopProgramsSummary topProgramsData;
|
||||
|
||||
private final UserActivitySummary userActivityData;
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*/
|
||||
public UserActivityPanel() {
|
||||
this(new TopProgramsSummary(), new UserActivitySummary());
|
||||
this(new UserActivitySummary());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*
|
||||
* @param topProgramsData Class from which to obtain top programs data.
|
||||
* @param userActivityData Class from which to obtain remaining user
|
||||
* activity data.
|
||||
*/
|
||||
public UserActivityPanel(
|
||||
TopProgramsSummary topProgramsData,
|
||||
UserActivitySummary userActivityData) {
|
||||
|
||||
super(topProgramsData, userActivityData);
|
||||
|
||||
this.topProgramsData = topProgramsData;
|
||||
public UserActivityPanel(UserActivitySummary userActivityData) {
|
||||
super(userActivityData);
|
||||
this.userActivityData = userActivityData;
|
||||
|
||||
// set up data acquisition methods
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// top programs query
|
||||
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(result) -> {
|
||||
showResultWithModuleCheck(topProgramsTable, result,
|
||||
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
|
||||
@@ -307,7 +301,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
* @return The underlying short folder name if one exists.
|
||||
*/
|
||||
private String getShortFolderName(String path, String appName) {
|
||||
return this.topProgramsData.getShortFolderName(path, appName);
|
||||
return this.userActivityData.getShortFolderName(path, appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -140,7 +140,7 @@ public class DataFetchWorker<A, R> extends SwingWorker<R, Void> {
|
||||
}
|
||||
|
||||
// and pass the result to the client
|
||||
resultHandler.accept(DataFetchResult.getErrorResult(inner));
|
||||
resultHandler.accept(DataFetchResult.getErrorResult(ex.getCause()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Map;
|
||||
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
@@ -35,12 +36,14 @@ public class DomainSearch {
|
||||
|
||||
private final DomainSearchCache searchCache;
|
||||
private final DomainSearchThumbnailCache thumbnailCache;
|
||||
private final DomainSearchArtifactsCache artifactsCache;
|
||||
|
||||
/**
|
||||
* Construct a new DomainSearch object.
|
||||
*/
|
||||
public DomainSearch() {
|
||||
this(new DomainSearchCache(), new DomainSearchThumbnailCache());
|
||||
this(new DomainSearchCache(), new DomainSearchThumbnailCache(),
|
||||
new DomainSearchArtifactsCache());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,9 +54,11 @@ public class DomainSearch {
|
||||
* @param thumbnailCache The DomainSearchThumnailCache to use for this
|
||||
* DomainSearch.
|
||||
*/
|
||||
DomainSearch(DomainSearchCache cache, DomainSearchThumbnailCache thumbnailCache) {
|
||||
DomainSearch(DomainSearchCache cache, DomainSearchThumbnailCache thumbnailCache,
|
||||
DomainSearchArtifactsCache artifactsCache) {
|
||||
this.searchCache = cache;
|
||||
this.thumbnailCache = thumbnailCache;
|
||||
this.artifactsCache = artifactsCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,17 +144,40 @@ public class DomainSearch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a thumbnail representation of a domain name. See
|
||||
* DomainSearchThumbnailRequest for more details.
|
||||
* Get a thumbnail representation of a domain name.
|
||||
*
|
||||
* Thumbnail candidates are JPEG files that have either TSK_WEB_DOWNLOAD or
|
||||
* TSK_WEB_CACHE artifacts that match the domain name (see the DomainSearch
|
||||
* getArtifacts() API). JPEG files are sorted by most recent if sourced from
|
||||
* TSK_WEB_DOWNLOADs and by size if sourced from TSK_WEB_CACHE artifacts.
|
||||
* The first suitable thumbnail is selected.
|
||||
*
|
||||
* @param thumbnailRequest Thumbnail request for domain.
|
||||
*
|
||||
* @return An Image instance or null if no thumbnail is available.
|
||||
* @return A thumbnail of the first matching JPEG, or a default thumbnail if
|
||||
* no suitable JPEG exists.
|
||||
*
|
||||
* @throws DiscoveryException If there is an error with Discovery related
|
||||
* processing.
|
||||
* processing.
|
||||
*/
|
||||
public Image getThumbnail(DomainSearchThumbnailRequest thumbnailRequest) throws DiscoveryException {
|
||||
return thumbnailCache.get(thumbnailRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all blackboard artifacts that match the requested domain name.
|
||||
*
|
||||
* Artifacts will be selected if the requested domain name is either an
|
||||
* exact match on a TSK_DOMAIN value or a substring match on a TSK_URL
|
||||
* value. String matching is case insensitive.
|
||||
*
|
||||
* @param artifactsRequest The request containing the case, artifact type,
|
||||
* and domain name.
|
||||
* @return A list of blackboard artifacts that match the request criteria.
|
||||
* @throws DiscoveryException If an exception is encountered during
|
||||
* processing.
|
||||
*/
|
||||
public List<BlackboardArtifact> getArtifacts(DomainSearchArtifactsRequest artifactsRequest) throws DiscoveryException {
|
||||
return artifactsCache.get(artifactsRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ public class DomainSearchArtifactsCache {
|
||||
* process.
|
||||
*/
|
||||
public List<BlackboardArtifact> get(DomainSearchArtifactsRequest request) throws DiscoveryException {
|
||||
String typeName = request.getArtifactType().getLabel();
|
||||
if (!typeName.startsWith("TSK_WEB")) {
|
||||
throw new IllegalArgumentException("Only web artifacts are valid arguments");
|
||||
}
|
||||
|
||||
try {
|
||||
return cache.get(request);
|
||||
} catch (ExecutionException ex) {
|
||||
|
||||
@@ -39,10 +39,10 @@ import org.openide.util.ImageUtilities;
|
||||
|
||||
/**
|
||||
* Loads a thumbnail for the given request. Thumbnail candidates are JPEG files
|
||||
* that are either TSK_WEB_DOWNLOAD or TSK_WEB_CACHE artifacts. JPEG files are
|
||||
* sorted by most recent if sourced from TSK_WEB_DOWNLOADs. JPEG files are
|
||||
* sorted by size if sourced from TSK_WEB_CACHE artifacts. Artifacts are first
|
||||
* loaded from the DomainSearchArtifactsCache and then further analyzed.
|
||||
* that have either TSK_WEB_DOWNLOAD or TSK_WEB_CACHE artifacts that match the
|
||||
* domain name (see the DomainSearch getArtifacts() API). JPEG files are sorted
|
||||
* by most recent if sourced from TSK_WEB_DOWNLOADs and by size if sourced from
|
||||
* TSK_WEB_CACHE artifacts. The first suitable thumbnail is selected.
|
||||
*/
|
||||
public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbnailRequest, Image> {
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -183,6 +183,7 @@ public class HEICProcessor implements PictureProcessor {
|
||||
final Path outputFile = moduleOutputFolder.resolve(baseFileName + ".jpg");
|
||||
|
||||
final Path imageMagickErrorOutput = moduleOutputFolder.resolve(IMAGE_MAGICK_ERROR_FILE);
|
||||
Files.deleteIfExists(imageMagickErrorOutput);
|
||||
Files.createFile(imageMagickErrorOutput);
|
||||
|
||||
// ImageMagick will write the primary image to the output file.
|
||||
|
||||
@@ -379,7 +379,10 @@ public class HTMLReport implements TableReportModule {
|
||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/validationFailed.png"); //NON-NLS
|
||||
break;
|
||||
case TSK_WEB_ACCOUNT_TYPE:
|
||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/web-account-type.png.png"); //NON-NLS
|
||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/web-account-type.png"); //NON-NLS
|
||||
break;
|
||||
case TSK_WEB_FORM_ADDRESS:
|
||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/web-form-address.png"); //NON-NLS
|
||||
break;
|
||||
default:
|
||||
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
|
||||
|
||||
+2
@@ -83,6 +83,7 @@ public class PortableCaseReportModuleSettings implements ReportModuleSettings {
|
||||
this.chunkSize = ChunkSize.NONE;
|
||||
this.allTagsSelected = true;
|
||||
this.allSetsSelected = true;
|
||||
this.shouldIncludeApplication = false;
|
||||
}
|
||||
|
||||
PortableCaseReportModuleSettings(List<String> setNames, List<TagName> tagNames,
|
||||
@@ -93,6 +94,7 @@ public class PortableCaseReportModuleSettings implements ReportModuleSettings {
|
||||
this.chunkSize = chunkSize;
|
||||
this.allTagsSelected = allTagsSelected;
|
||||
this.allSetsSelected = allSetsSelected;
|
||||
this.shouldIncludeApplication = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.sleuthkit.autopsy.testutils.TskMockUtils;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE;
|
||||
@@ -70,7 +70,7 @@ public class GetArtifactsTest {
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private final void test(BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType,
|
||||
private void test(BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType,
|
||||
SortOrder sortOrder, int count, List<BlackboardArtifact> returnArr, TskCoreException blackboardEx,
|
||||
List<BlackboardArtifact> expectedArr, Class<? extends Exception> expectedException) throws TskCoreException {
|
||||
|
||||
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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 org.sleuthkit.autopsy.discovery.search;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
public class DomainSearchArtifactsCacheTest {
|
||||
|
||||
private static final ARTIFACT_TYPE WEB_ARTIFACT_TYPE = ARTIFACT_TYPE.TSK_WEB_BOOKMARK;
|
||||
private static final BlackboardAttribute.Type TSK_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
|
||||
private static final BlackboardAttribute.Type TSK_URL = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL);
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void get_NonWebArtifactType_ShouldThrow() throws DiscoveryException {
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(null, "google.com", ARTIFACT_TYPE.TSK_CALLLOG);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
cache.get(request);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test is important for ensuring artifact loading can
|
||||
* be cancelled, which is necessary for a responsive UI.
|
||||
*/
|
||||
@Test
|
||||
public void get_ThreadInterrupted_ShouldThrow() throws TskCoreException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "facebook.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
cache.get(request);
|
||||
// Clear the interrupt flag on failure.
|
||||
Thread.interrupted();
|
||||
Assert.fail("Should have thrown a discovery exception.");
|
||||
} catch (DiscoveryException ex) {
|
||||
// Clear the interrupt flag on success (or failure).
|
||||
Thread.interrupted();
|
||||
Assert.assertEquals(InterruptedException.class, ex.getCause().getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_MatchingDomain_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_DOMAIN)).thenReturn(mockDomainAttribute("google.com"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "google.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_MatchingUrl_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_URL)).thenReturn(mockURLAttribute("https://www.abc.com/search"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "abc.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_MismatchedDomainName_ShouldBeEmpty() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_DOMAIN)).thenReturn(mockDomainAttribute("google.com"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "facebook.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(0, artifacts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_MismatchedUrl_ShouldBeEmpty() throws DiscoveryException, TskCoreException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_URL)).thenReturn(mockURLAttribute("https://www.dce1.com/search"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "dce.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(0, artifacts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_CaseInsensitiveDomainAttribute_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_DOMAIN)).thenReturn(mockDomainAttribute("xYZ.coM"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "xyz.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_CaseInsensitiveRequestDomain_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_DOMAIN)).thenReturn(mockDomainAttribute("google.com"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "GooGle.coM", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_CaseInsensitiveUrlAttribute_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_URL)).thenReturn(mockURLAttribute("https://www.JfK.coM/search"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "jfk.com", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get_CaseInsensitiveRequestUrl_ShouldHaveSizeOne() throws TskCoreException, DiscoveryException {
|
||||
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||
BlackboardArtifact mockArtifact = mock(BlackboardArtifact.class);
|
||||
when(mockArtifact.getAttribute(TSK_URL)).thenReturn(mockURLAttribute("https://www.hop.com/search"));
|
||||
when(mockCase.getBlackboardArtifacts(WEB_ARTIFACT_TYPE)).thenReturn(Lists.newArrayList(mockArtifact));
|
||||
|
||||
DomainSearchArtifactsRequest request = new DomainSearchArtifactsRequest(mockCase, "HoP.cOm", WEB_ARTIFACT_TYPE);
|
||||
DomainSearchArtifactsCache cache = new DomainSearchArtifactsCache();
|
||||
List<BlackboardArtifact> artifacts = cache.get(request);
|
||||
Assert.assertEquals(1, artifacts.size());
|
||||
Assert.assertEquals(mockArtifact, artifacts.get(0));
|
||||
}
|
||||
|
||||
private BlackboardAttribute mockDomainAttribute(String value) {
|
||||
return new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, "", value);
|
||||
}
|
||||
|
||||
private BlackboardAttribute mockURLAttribute(String value) {
|
||||
return new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, "", value);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class DomainSearchTest {
|
||||
};
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
Map<GroupKey, Integer> sizes = domainSearch.getGroupSizes(null,
|
||||
new ArrayList<>(), null, null, null, null, null);
|
||||
assertEquals(4, sizes.get(groupOne).longValue());
|
||||
@@ -83,7 +83,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
Map<GroupKey, Integer> sizes = domainSearch.getGroupSizes(null,
|
||||
new ArrayList<>(), null, null, null, null, null);
|
||||
assertEquals(4, sizes.get(groupOne).longValue());
|
||||
@@ -97,7 +97,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(new HashMap<>());
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
Map<GroupKey, Integer> sizes = domainSearch.getGroupSizes(null,
|
||||
new ArrayList<>(), null, null, null, null, null);
|
||||
assertEquals(0, sizes.size());
|
||||
@@ -122,7 +122,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 0, 3, null, null);
|
||||
assertEquals(3, firstPage.size());
|
||||
@@ -150,7 +150,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 0, 100, null, null);
|
||||
assertEquals(4, firstPage.size());
|
||||
@@ -178,7 +178,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 0, 2, null, null);
|
||||
assertEquals(2, firstPage.size());
|
||||
@@ -206,7 +206,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 3, 1, null, null);
|
||||
assertEquals(1, firstPage.size());
|
||||
@@ -232,7 +232,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 20, 5, null, null);
|
||||
assertEquals(0, firstPage.size());
|
||||
@@ -257,7 +257,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 0, 0, null, null);
|
||||
assertEquals(0, firstPage.size());
|
||||
@@ -292,7 +292,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupOne, 0, 3, null, null);
|
||||
assertEquals(3, firstPage.size());
|
||||
@@ -327,7 +327,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
List<Result> firstPage = domainSearch.getDomainsInGroup(null,
|
||||
new ArrayList<>(), null, null, null, groupTwo, 1, 2, null, null);
|
||||
assertEquals(2, firstPage.size());
|
||||
@@ -359,7 +359,7 @@ public class DomainSearchTest {
|
||||
|
||||
when(cache.get(null, new ArrayList<>(), null, null, null, null, null)).thenReturn(dummyData);
|
||||
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null);
|
||||
DomainSearch domainSearch = new DomainSearch(cache, null, null);
|
||||
|
||||
int start = 0;
|
||||
int size = 2;
|
||||
|
||||
@@ -133,4 +133,7 @@ public class TskMockUtils {
|
||||
doNothing().when(logger.log(any(Level.class), any(Throwable.class)));
|
||||
return logger;
|
||||
}
|
||||
|
||||
private TskMockUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user