Merge pull request #6475 from eugene7646/solr-8-upgrade

Merged develop
This commit is contained in:
eugene7646 2020-11-19 11:44:57 -05:00 committed by GitHub
commit 80bc0a5434
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1217 additions and 436 deletions

View File

@ -382,6 +382,31 @@ public class CentralRepoDbManager {
CentralRepoDbUtil.setUseCentralRepo(true); CentralRepoDbUtil.setUseCentralRepo(true);
saveNewCentralRepo(); saveNewCentralRepo();
} }
/**
* Set up a PostgresDb using the settings for the given database choice
* enum.
*
* @param choice Type of postgres DB to set up
* @throws CentralRepoException
*/
public void setupPostgresDb(CentralRepoDbChoice choice) throws CentralRepoException {
selectedDbChoice = choice;
DatabaseTestResult curStatus = testStatus();
if (curStatus == DatabaseTestResult.DB_DOES_NOT_EXIST) {
createDb();
curStatus = testStatus();
}
// the only successful setup status is tested ok
if (curStatus != DatabaseTestResult.TESTED_OK) {
throw new CentralRepoException("Unable to successfully create postgres database. Test failed with: " + curStatus);
}
// if successfully got here, then save the settings
CentralRepoDbUtil.setUseCentralRepo(true);
saveNewCentralRepo();
}
/** /**
* This method returns if changes to the central repository configuration * This method returns if changes to the central repository configuration

View File

@ -121,10 +121,12 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
* @return * @return
*/ */
String getConnectionURL(boolean usePostgresDb) { String getConnectionURL(boolean usePostgresDb) {
StringBuilder url = new StringBuilder(); StringBuilder url = new StringBuilder()
url.append(getJDBCBaseURI()); .append(getJDBCBaseURI())
url.append(getHost()); .append(getHost())
url.append("/"); // NON-NLS .append(":") // NON-NLS
.append(getPort())
.append("/"); // NON-NLS
if (usePostgresDb) { if (usePostgresDb) {
url.append("postgres"); // NON-NLS url.append("postgres"); // NON-NLS
} else { } else {
@ -153,7 +155,7 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
} catch (ClassNotFoundException | SQLException ex) { } catch (ClassNotFoundException | SQLException ex) {
// TODO: Determine why a connection failure (ConnectionException) re-throws // TODO: Determine why a connection failure (ConnectionException) re-throws
// the SQLException and does not print this log message? // the SQLException and does not print this log message?
LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql."); // NON-NLS LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql.", ex); // NON-NLS
conn = null; conn = null;
} }
return conn; return conn;

View File

@ -1,4 +1,10 @@
caseeventlistener.evidencetag=Evidence caseeventlistener.evidencetag=Evidence
CentralRepositoryNotificationDialog.bulletHeader=This data is used to:
CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts)
CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts
CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen
CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel.
CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository.
IngestEventsListener.ingestmodule.name=Central Repository IngestEventsListener.ingestmodule.name=Central Repository
IngestEventsListener.prevCaseComment.text=Previous Case: IngestEventsListener.prevCaseComment.text=Previous Case:
# {0} - typeName # {0} - typeName
@ -7,6 +13,3 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository) IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
Installer.centralRepoUpgradeFailed.title=Central repository disabled Installer.centralRepoUpgradeFailed.title=Central repository disabled
Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. You can use this to ignore previously seen files and make connections between cases.
Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?
Installer.initialCreateSqlite.title=Enable Central Repository?

View File

@ -0,0 +1,73 @@
/*
* Central Repository
*
* 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.centralrepository.eventlisteners;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.Version;
/**
* Notifies new installations or old upgrades that the central repository will
* be enabled by default.
*/
public class CentralRepositoryNotificationDialog {
/**
* This dialog should display iff the mode is RELEASE and the
* application is running with a GUI.
*/
static boolean shouldDisplay() {
return Version.getBuildType() == Version.Type.RELEASE
&& RuntimeProperties.runningWithGUI();
}
/**
* Displays an informational modal dialog to the user, which is dismissed by
* pressing 'OK'.
*/
@NbBundle.Messages({
"CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository.",
"CentralRepositoryNotificationDialog.bulletHeader=This data is used to:",
"CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts)",
"CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen",
"CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts",
"CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel."
})
static void display() {
assert shouldDisplay();
MessageNotifyUtil.Message.info(
"<html>"
+ "<body>"
+ "<div>"
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_header() + "</p>"
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_bulletHeader() + "</p>"
+ "<ul>"
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletOne() + "</li>"
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletTwo() + "</li>"
+ "<li>" + Bundle.CentralRepositoryNotificationDialog_bulletThree() + "</li>"
+ "</ul>"
+ "<p>" + Bundle.CentralRepositoryNotificationDialog_finalRemarks() + "</p>"
+ "</div>"
+ "</body>"
+ "</html>"
);
}
}

View File

@ -25,14 +25,13 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.openide.modules.ModuleInstall; import org.openide.modules.ModuleInstall;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.Version;
/** /**
* Adds/removes application event listeners responsible for adding data to the * Adds/removes application event listeners responsible for adding data to the
@ -81,19 +80,10 @@ public class Installer extends ModuleInstall {
* the org.sleuthkit.autopsy.core package when the already installed * the org.sleuthkit.autopsy.core package when the already installed
* Autopsy-Core module is restored (during application startup). * Autopsy-Core module is restored (during application startup).
*/ */
@NbBundle.Messages({
"Installer.initialCreateSqlite.title=Enable Central Repository?",
"Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?",
"Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. "
+ "You can use this to ignore previously seen files and make connections between cases."
})
@Override @Override
public void restored() { public void restored() {
addApplicationEventListeners(); addApplicationEventListeners();
setupDefaultCentralRepository();
if (Version.getBuildType() == Version.Type.RELEASE) {
setupDefaultCentralRepository();
}
} }
/** /**
@ -107,9 +97,9 @@ public class Installer extends ModuleInstall {
/** /**
* Checks if the central repository has been set up and configured. If not, * Checks if the central repository has been set up and configured. If not,
* either offers to perform set up (running with a GUI) or does the set up * does the set up unconditionally. If the application is running with a
* unconditionally (not running with a GUI, e.g., in an automated ingest * GUI, a notification will be displayed to the user if the mode is RELEASE
* node). * (in other words, developers are exempt from seeing the notification).
*/ */
private void setupDefaultCentralRepository() { private void setupDefaultCentralRepository() {
Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository"); Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository");
@ -127,62 +117,30 @@ public class Installer extends ModuleInstall {
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true"); ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
} }
} }
// if central repository hasn't been previously initialized, initialize it if(initialized) {
if (!initialized) { return; // Nothing to do
// if running with a GUI, prompt the user
if (RuntimeProperties.runningWithGUI()) {
try {
SwingUtilities.invokeAndWait(() -> {
try {
String dialogText
= "<html><body>"
+ "<div style='width: 400px;'>"
+ "<p>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageHeader") + "</p>"
+ "<p style='margin-top: 10px'>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageDesc") + "</p>"
+ "</div>"
+ "</body></html>";
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
dialogText,
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"),
JOptionPane.YES_NO_OPTION)) {
setupDefaultSqliteCentralRepo();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(ex);
}
});
} catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, "There was an error while running the swing utility invoke later while creating the central repository database", ex);
}
} // if no GUI, just initialize
else {
try {
setupDefaultSqliteCentralRepo();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(ex);
}
}
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
} }
}
/** if (CentralRepositoryNotificationDialog.shouldDisplay()) {
* Sets up a default single-user SQLite central repository. CentralRepositoryNotificationDialog.display();
* }
* @throws CentralRepoException If there is an error setting up teh central
* repository. try {
*/ CentralRepoDbManager manager = new CentralRepoDbManager();
private void setupDefaultSqliteCentralRepo() throws CentralRepoException { if (UserPreferences.getIsMultiUserModeEnabled()) {
CentralRepoDbManager manager = new CentralRepoDbManager(); // Set up using existing multi-user settings.
manager.setupDefaultSqliteDb(); manager.setupPostgresDb(CentralRepoDbChoice.POSTGRESQL_MULTIUSER);
} else {
manager.setupDefaultSqliteDb();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(ex);
}
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
} }
/** /**

View File

@ -28,10 +28,11 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.stream.Collectors;
import java.util.TreeMap; import org.apache.commons.lang.StringUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -93,16 +94,56 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return ARTIFACT_UPDATE_TYPE_IDS; return ARTIFACT_UPDATE_TYPE_IDS;
} }
/**
* Removes fileDetails entries with redundant paths, sorts by date
* descending and limits to the limit provided.
*
* @param fileDetails The file details list.
* @param limit The maximum number of entries to return.
* @return The sorted limited list with unique paths.
*/
private <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
Map<String, T> fileDetailsMap = fileDetails.stream()
.filter(details -> details != null)
.collect(Collectors.toMap(
d -> d.getPath().toUpperCase(),
d -> d,
(d1, d2) -> Long.compare(d1.getDateAsLong(), d2.getDateAsLong()) > 0 ? d1 : d2));
return fileDetailsMap.values().stream()
.sorted((a, b) -> -Long.compare(a.getDateAsLong(), b.getDateAsLong()))
.limit(limit)
.collect(Collectors.toList());
}
/**
* Returns a RecentFileDetails object as derived from the recent document
* artifact or null if no appropriate object can be made.
*
* @param artifact The artifact.
* @return The derived object or null if artifact is invalid.
*/
private RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) {
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
Long lastOpened = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ATT);
if (StringUtils.isBlank(path) || lastOpened == null || lastOpened == 0) {
return null;
} else {
return new RecentFileDetails(path, lastOpened);
}
}
/** /**
* Return a list of the most recently opened documents based on the * Return a list of the most recently opened documents based on the
* TSK_RECENT_OBJECT artifact. * TSK_RECENT_OBJECT artifact.
* *
* @param dataSource The data source to query. * @param dataSource The data source to query.
* @param maxCount The maximum number of results to return, pass 0 to get * @param maxCount The maximum number of results to return, pass 0 to get a
* a list of all results. * list of all results.
* *
* @return A list RecentFileDetails representing the most recently opened * @return A list RecentFileDetails representing the most recently opened
* documents or an empty list if none were found. * documents or an empty list if none were found.
* *
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
@ -112,36 +153,45 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
List<BlackboardArtifact> artifactList throwOnNonPositiveCount(maxCount);
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT),
dataSource,
DATETIME_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
List<RecentFileDetails> fileDetails = new ArrayList<>(); List<RecentFileDetails> details = provider.get().getBlackboard()
for (BlackboardArtifact artifact : artifactList) { .getArtifacts(ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), dataSource.getId()).stream()
Long accessedTime = null; .map(art -> getRecentlyOpenedDocument(art))
String path = ""; .filter(d -> d != null)
.collect(Collectors.toList());
// Get all the attributes in one call. return getSortedLimited(details, maxCount);
List<BlackboardAttribute> attributeList = artifact.getAttributes(); }
for (BlackboardAttribute attribute : attributeList) {
if (attribute.getAttributeType().equals(DATETIME_ATT)) { /**
accessedTime = attribute.getValueLong(); * Returns a RecentDownloadDetails object as derived from the recent
} else if (attribute.getAttributeType().equals(PATH_ATT)) { * download artifact or null if no appropriate object can be made.
path = attribute.getValueString(); *
} * @param artifact The artifact.
} * @return The derived object or null if artifact is invalid.
*/
private RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) {
Long accessedTime = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT);
String domain = DataSourceInfoUtilities.getStringOrNull(artifact, DOMAIN_ATT);
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
if (accessedTime != null && accessedTime != 0) { if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
fileDetails.add(new RecentFileDetails(path, accessedTime)); return null;
} } else {
return new RecentDownloadDetails(path, accessedTime, domain);
} }
}
return fileDetails; /**
* Throws an IllegalArgumentException if count is less than 1.
*
* @param count The count.
*/
private void throwOnNonPositiveCount(int count) {
if (count < 1) {
throw new IllegalArgumentException("Invalid count: value must be greater than 0.");
}
} }
/** /**
@ -149,11 +199,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* artifact TSK_DATETIME_ACCESSED attribute. * artifact TSK_DATETIME_ACCESSED attribute.
* *
* @param dataSource Data source to query. * @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will * @param maxCount Maximum number of results to return, passing 0 will
* return all results. * return all results.
* *
* @return A list of RecentFileDetails objects or empty list if none were * @return A list of RecentFileDetails objects or empty list if none were
* found. * found.
* *
* @throws TskCoreException * @throws TskCoreException
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
@ -163,46 +213,23 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
List<BlackboardArtifact> artifactList throwOnNonPositiveCount(maxCount);
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD),
dataSource,
DATETIME_ACCESSED_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
List<RecentDownloadDetails> fileDetails = new ArrayList<>(); List<RecentDownloadDetails> details = provider.get().getBlackboard()
for (BlackboardArtifact artifact : artifactList) { .getArtifacts(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), dataSource.getId()).stream()
// Get all the attributes in one call. .map(art -> getRecentDownload(art))
Long accessedTime = null; .filter(d -> d != null)
String domain = ""; .collect(Collectors.toList());
String path = "";
List<BlackboardAttribute> attributeList = artifact.getAttributes(); return getSortedLimited(details, maxCount);
for (BlackboardAttribute attribute : attributeList) {
if (attribute.getAttributeType().equals(DATETIME_ACCESSED_ATT)) {
accessedTime = attribute.getValueLong();
} else if (attribute.getAttributeType().equals(DOMAIN_ATT)) {
domain = attribute.getValueString();
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
path = attribute.getValueString();
}
}
if (accessedTime != null && accessedTime != 0L) {
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
}
}
return fileDetails;
} }
/** /**
* Returns a list of the most recent message attachments. * Returns a list of the most recent message attachments.
* *
* @param dataSource Data source to query. * @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will * @param maxCount Maximum number of results to return, passing 0 will
* return all results. * return all results.
* *
* @return A list of RecentFileDetails of the most recent attachments. * @return A list of RecentFileDetails of the most recent attachments.
* *
@ -214,115 +241,72 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
if (maxCount < 0) { throwOnNonPositiveCount(maxCount);
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
SleuthkitCase skCase = provider.get();
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard()
.getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId());
List<RecentAttachmentDetails> details = new ArrayList<>();
for (BlackboardArtifact artifact : associatedArtifacts) {
RecentAttachmentDetails thisDetails = getRecentAttachment(artifact, skCase);
if (thisDetails != null) {
details.add(thisDetails);
}
} }
return createListFromMap(buildAttachmentMap(dataSource), maxCount); return getSortedLimited(details, maxCount);
} }
/** /**
* Build a map of all of the message attachment sorted in date order. * Creates a RecentAttachmentDetails object from the associated object
* artifact or null if no RecentAttachmentDetails object can be derived.
* *
* @param dataSource Data source to query. * @param artifact The associated object artifact.
* * @param skCase The current case.
* @return Returns a SortedMap of details objects returned in descending * @return The derived object or null.
* order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
*/ */
private SortedMap<Long, List<RecentAttachmentDetails>> buildAttachmentMap(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
SleuthkitCase skCase = provider.get(); // get associated artifact or return no result
TreeMap<Long, List<RecentAttachmentDetails>> sortedMap = new TreeMap<>(); BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
if (attribute == null) {
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard().getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId()); return null;
for (BlackboardArtifact artifact : associatedArtifacts) {
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
if (attribute == null) {
continue;
}
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
if (messageArtifact != null && isMessageArtifact(messageArtifact)) {
Content content = artifact.getParent();
if (content instanceof AbstractFile) {
String sender;
Long date = null;
String path;
BlackboardAttribute senderAttribute = messageArtifact.getAttribute(EMAIL_FROM_ATT);
if (senderAttribute != null) {
sender = senderAttribute.getValueString();
} else {
sender = "";
}
senderAttribute = messageArtifact.getAttribute(MSG_DATEIME_SENT_ATT);
if (senderAttribute != null) {
date = senderAttribute.getValueLong();
}
AbstractFile abstractFile = (AbstractFile) content;
path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
if (date != null && date != 0) {
List<RecentAttachmentDetails> list = sortedMap.get(date);
if (list == null) {
list = new ArrayList<>();
sortedMap.put(date, list);
}
RecentAttachmentDetails details = new RecentAttachmentDetails(path, date, sender);
if (!list.contains(details)) {
list.add(details);
}
}
}
}
}
return sortedMap.descendingMap();
}
/**
* Create a list of detail objects from the given sorted map of the max
* size.
*
* @param sortedMap A Map of attachment details sorted by date.
* @param maxCount Maximum number of values to return.
*
* @return A list of the details of the most recent attachments or empty
* list if none where found.
*/
private List<RecentAttachmentDetails> createListFromMap(SortedMap<Long, List<RecentAttachmentDetails>> sortedMap, int maxCount) {
List<RecentAttachmentDetails> fileList = new ArrayList<>();
for (List<RecentAttachmentDetails> mapList : sortedMap.values()) {
if (maxCount == 0 || fileList.size() + mapList.size() <= maxCount) {
fileList.addAll(mapList);
continue;
}
if (maxCount == fileList.size()) {
break;
}
for (RecentAttachmentDetails details : mapList) {
if (fileList.size() < maxCount) {
fileList.add(details);
} else {
break;
}
}
} }
return fileList; // get associated message artifact if exists or return no result
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
if (messageArtifact == null || !isMessageArtifact(messageArtifact)) {
return null;
}
// get abstract file if exists or return no result
Content content = artifact.getParent();
if (!(content instanceof AbstractFile)) {
return null;
}
AbstractFile abstractFile = (AbstractFile) content;
// get the path, sender, and date
String path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
String sender = DataSourceInfoUtilities.getStringOrNull(messageArtifact, EMAIL_FROM_ATT);
Long date = DataSourceInfoUtilities.getLongOrNull(messageArtifact, MSG_DATEIME_SENT_ATT);
if (date == null || date == 0 || StringUtils.isBlank(path)) {
return null;
} else {
return new RecentAttachmentDetails(path, date, sender);
}
} }
/** /**
* Is the given artifact a message. * Is the given artifact a message.
* *
* @param nodeArtifact An artifact that might be a message. Must not be * @param nodeArtifact An artifact that might be a message. Must not be
* null. * null.
* *
* @return True if the given artifact is a message artifact * @return True if the given artifact is a message artifact
*/ */
@ -330,6 +314,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
final int artifactTypeID = nodeArtifact.getArtifactTypeID(); final int artifactTypeID = nodeArtifact.getArtifactTypeID();
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(); || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID();
} }
/** /**
@ -391,8 +376,8 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
/** /**
* Constructor for files with just a path and date. * Constructor for files with just a path and date.
* *
* @param path File path. * @param path File path.
* @param date File access date\time in seconds with java epoch. * @param date File access date\time in seconds with java epoch.
* @param webDomain The webdomain from which the file was downloaded. * @param webDomain The webdomain from which the file was downloaded.
*/ */
RecentDownloadDetails(String path, long date, String webDomain) { RecentDownloadDetails(String path, long date, String webDomain) {
@ -404,7 +389,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Returns the web domain. * Returns the web domain.
* *
* @return The web domain or empty string if not available or * @return The web domain or empty string if not available or
* applicable. * applicable.
*/ */
public String getWebDomain() { public String getWebDomain() {
return webDomain; return webDomain;
@ -422,10 +407,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Constructor for recent download files which have a path, date and * Constructor for recent download files which have a path, date and
* domain value. * domain value.
* *
* @param path File path. * @param path File path.
* @param date File crtime. * @param date File crtime.
* @param sender The sender of the message from which the file was * @param sender The sender of the message from which the file was
* attached. * attached.
*/ */
RecentAttachmentDetails(String path, long date, String sender) { RecentAttachmentDetails(String path, long date, String sender) {
super(path, date); super(path, date);
@ -436,7 +421,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Return the sender of the attached file. * Return the sender of the attached file.
* *
* @return The sender of the attached file or empty string if not * @return The sender of the attached file or empty string if not
* available. * available.
*/ */
public String getSender() { public String getSender() {
return sender; return sender;

View File

@ -137,7 +137,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return (a.getProgramName() == null ? "" : a.getProgramName()) return (a.getProgramName() == null ? "" : a.getProgramName())
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName())); .compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
}; };
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
@ -172,9 +172,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* is designed with unit testing in mind since mocked dependencies can be * is designed with unit testing in mind since mocked dependencies can be
* utilized. * utilized.
* *
* @param provider The object providing the current SleuthkitCase. * @param provider The object providing the current SleuthkitCase.
* @param translationService The translation service. * @param translationService The translation service.
* @param logger The logger to use. * @param logger The logger to use.
*/ */
public UserActivitySummary( public UserActivitySummary(
SleuthkitCaseProvider provider, SleuthkitCaseProvider provider,
@ -206,7 +206,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Gets a list of recent domains based on the datasource. * Gets a list of recent domains based on the datasource.
* *
* @param dataSource The datasource to query for recent domains. * @param dataSource The datasource to query for recent domains.
* @param count The max count of items to return. * @param count The max count of items to return.
* *
* @return The list of items retrieved from the database. * @return The list of items retrieved from the database.
* *
@ -242,12 +242,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Creates a TopDomainsResult from data or null if no visit date exists * Creates a TopDomainsResult from data or null if no visit date exists
* within DOMAIN_WINDOW_MS of mostRecentMs. * within DOMAIN_WINDOW_MS of mostRecentMs.
* *
* @param domain The domain. * @param domain The domain.
* @param visits The number of visits. * @param visits The number of visits.
* @param mostRecentMs The most recent visit of any domain. * @param mostRecentMs The most recent visit of any domain.
* *
* @return The TopDomainsResult or null if no visits to this domain within * @return The TopDomainsResult or null if no visits to this domain within
* 30 days of mostRecentMs. * 30 days of mostRecentMs.
*/ */
private TopDomainsResult getDomainsResult(String domain, List<Long> visits, long mostRecentMs) { private TopDomainsResult getDomainsResult(String domain, List<Long> visits, long mostRecentMs) {
long visitCount = 0; long visitCount = 0;
@ -280,9 +280,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dataSource The datasource. * @param dataSource The datasource.
* *
* @return A tuple where the first value is the latest web history accessed * @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized * date in milliseconds and the second value maps normalized (lowercase;
* (lowercase; trimmed) domain names to when those domains were * trimmed) domain names to when those domains were visited.
* visited.
* *
* @throws TskCoreException * @throws TskCoreException
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
@ -349,7 +348,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact. * @param artifact The artifact.
* *
* @return The TopWebSearchResult or null if the search string or date * @return The TopWebSearchResult or null if the search string or date
* accessed cannot be determined. * accessed cannot be determined.
*/ */
private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) { private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT); String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
@ -364,11 +363,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* term. * term.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent web searches where most recent search * @return The list of most recent web searches where most recent search
* appears first. * appears first.
* *
* @throws * @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -386,21 +384,22 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId()); .getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
// group by search string (case insensitive) // group by search string (case insensitive)
Collection<List<TopWebSearchResult>> resultGroups = webSearchArtifacts Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
.stream() .stream()
// get items where search string and date is not null // get items where search string and date is not null
.map(UserActivitySummary::getWebSearchResult) .map(UserActivitySummary::getWebSearchResult)
// remove null records // remove null records
.filter(result -> result != null) .filter(result -> result != null)
// get these messages grouped by search to string // get the latest message for each search string
.collect(Collectors.groupingBy((result) -> result.getSearchString().toUpperCase())) .collect(Collectors.toMap(
(result) -> result.getSearchString().toUpperCase(),
result -> result,
(result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values(); .values();
// get the most recent date for each search term // get the most recent date for each search term
List<TopWebSearchResult> results = resultGroups List<TopWebSearchResult> results = resultGroups
.stream() .stream()
// get the most recent access per search type
.map((list) -> list.stream().max(TOP_WEBSEARCH_RESULT_DATE_COMPARE).get())
// get most recent searches first // get most recent searches first
.sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed()) .sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
.limit(count) .limit(count)
@ -424,7 +423,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param original The original text. * @param original The original text.
* *
* @return The translated text or null if no translation can be determined * @return The translated text or null if no translation can be determined
* or exists. * or exists.
*/ */
private String getTranslationOrNull(String original) { private String getTranslationOrNull(String original) {
if (!translationService.hasProvider() || StringUtils.isBlank(original)) { if (!translationService.hasProvider() || StringUtils.isBlank(original)) {
@ -448,15 +447,34 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return translated; return translated;
} }
/**
* Gives the most recent TopDeviceAttachedResult. If one is null, the other
* is returned.
*
* @param r1 A result.
* @param r2 Another result.
* @return The most recent one with a non-null date.
*/
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
if (r2.getDateAccessed() == null) {
return r1;
}
if (r1.getDateAccessed() == null) {
return r2;
}
return r1.getDateAccessed().compareTo(r2.getDateAccessed()) >= 0 ? r1 : r2;
}
/** /**
* Retrieves most recent devices used by most recent date attached. * Retrieves most recent devices used by most recent date attached.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent devices attached where most recent device * @return The list of most recent devices attached where most recent device
* attached appears first. * attached appears first.
* *
* @throws * @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -469,7 +487,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList(); return Collections.emptyList();
} }
return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED, Collection<TopDeviceAttachedResult> results = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0) dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0)
.stream() .stream()
.map(artifact -> { .map(artifact -> {
@ -482,9 +500,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
}) })
// remove Root Hub identifier // remove Root Hub identifier
.filter(result -> { .filter(result -> {
return result.getDeviceModel() == null return result.getDeviceId() == null
|| result.getDeviceModel() == null
|| !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase()); || !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase());
}) })
.collect(Collectors.toMap(result -> result.getDeviceId(), result -> result, (r1, r2) -> getMostRecentDevice(r1, r2)))
.values();
return results.stream()
.limit(count) .limit(count)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -495,7 +518,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact. * @param artifact The artifact.
* *
* @return The TopAccountResult or null if the account type or message date * @return The TopAccountResult or null if the account type or message date
* cannot be determined. * cannot be determined.
*/ */
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) { private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE); String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
@ -509,12 +532,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Obtains a TopAccountResult from a blackboard artifact. The date is * Obtains a TopAccountResult from a blackboard artifact. The date is
* maximum of any found dates for attribute types provided. * maximum of any found dates for attribute types provided.
* *
* @param artifact The artifact. * @param artifact The artifact.
* @param messageType The type of message this is. * @param messageType The type of message this is.
* @param dateAttrs The date attribute types. * @param dateAttrs The date attribute types.
* *
* @return The TopAccountResult or null if the account type or max date are * @return The TopAccountResult or null if the account type or max date are
* not provided. * not provided.
*/ */
private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) { private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
String type = messageType; String type = messageType;
@ -538,11 +561,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* sent. * sent.
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > * @param count The maximum number of records to be shown (must be > 0).
* 0).
* *
* @return The list of most recent accounts used where the most recent * @return The list of most recent accounts used where the most recent
* account by last message sent occurs first. * account by last message sent occurs first.
* *
* @throws * @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -585,18 +607,19 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults)); Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults));
// get them grouped by account type // get them grouped by account type
Collection<List<TopAccountResult>> groupedResults = allResults Collection<TopAccountResult> groupedResults = allResults
// remove null records // remove null records
.filter(result -> result != null) .filter(result -> result != null)
// get these messages grouped by account type // get these messages grouped by account type and get the most recent of each type
.collect(Collectors.groupingBy(TopAccountResult::getAccountType)) .collect(Collectors.toMap(
result -> result.getAccountType(),
result -> result,
(result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values(); .values();
// get account type sorted by most recent date // get account type sorted by most recent date
return groupedResults return groupedResults
.stream() .stream()
// get the most recent access per account type
.map((accountGroup) -> accountGroup.stream().max(TOP_ACCOUNT_RESULT_DATE_COMPARE).get())
// get most recent accounts accessed // get most recent accounts accessed
.sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed()) .sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
// limit to count // limit to count
@ -608,7 +631,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/** /**
* Determines a short folder name if any. Otherwise, returns empty string. * Determines a short folder name if any. Otherwise, returns empty string.
* *
* @param strPath The string path. * @param strPath The string path.
* @param applicationName The application name. * @param applicationName The application name.
* *
* @return The short folder name or empty string if not found. * @return The short folder name or empty string if not found.
@ -659,7 +682,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) { if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
return null; return null;
} }
Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT); Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
Long longCount = (count == null) ? null : (long) count; Long longCount = (count == null) ? null : (long) count;
@ -696,7 +719,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param long2 Second possibly null long. * @param long2 Second possibly null long.
* *
* @return Returns the compare value: 1,0,-1 favoring the higher non-null * @return Returns the compare value: 1,0,-1 favoring the higher non-null
* value. * value.
*/ */
private static int nullableCompare(Long long1, Long long2) { private static int nullableCompare(Long long1, Long long2) {
if (long1 == null && long2 == null) { if (long1 == null && long2 == null) {
@ -721,7 +744,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return longNum != null && longNum > 0; return longNum != null && longNum > 0;
} }
/** /**
* Retrieves the top programs results for the given data source limited to * 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 * the count provided as a parameter. The highest run times are at the top
@ -731,12 +753,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* be ignored and all items will be returned. * be ignored and all items will be returned.
* *
* @param dataSource The datasource. If the datasource is null, an empty * @param dataSource The datasource. If the datasource is null, an empty
* list will be returned. * list will be returned.
* @param count The number of results to return. This value must be > 0 * @param count The number of results to return. This value must be > 0 or
* or an IllegalArgumentException will be thrown. * an IllegalArgumentException will be thrown.
* *
* @return The sorted list and limited to the count if last run or run count * @return The sorted list and limited to the count if last run or run count
* information is available on any item. * information is available on any item.
* *
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
@ -759,7 +781,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// The value will be a TopProgramsResult with the max run times // The value will be a TopProgramsResult with the max run times
// and most recent last run date for each program name / program path pair. // and most recent last run date for each program name / program path pair.
.collect(Collectors.toMap( .collect(Collectors.toMap(
res -> Pair.of(res.getProgramName(), res.getProgramPath()), res -> Pair.of(
res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
res -> res, res -> res,
(res1, res2) -> { (res1, res2) -> {
return new TopProgramsResult( return new TopProgramsResult(
@ -852,10 +876,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param deviceId The device id. * @param deviceId The device id.
* @param dateAccessed The date last attached. * @param dateAccessed The date last attached.
* @param deviceMake The device make. * @param deviceMake The device make.
* @param deviceModel The device model. * @param deviceModel The device model.
*/ */
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) { public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) {
this.deviceId = deviceId; this.deviceId = deviceId;
@ -906,7 +930,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param accountType The account type. * @param accountType The account type.
* @param lastAccess The date the account was last accessed. * @param lastAccess The date the account was last accessed.
*/ */
public TopAccountResult(String accountType, Date lastAccess) { public TopAccountResult(String accountType, Date lastAccess) {
this.accountType = accountType; this.accountType = accountType;
@ -940,9 +964,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/** /**
* Describes a top domain result. * Describes a top domain result.
* *
* @param domain The domain. * @param domain The domain.
* @param visitTimes The number of times it was visited. * @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit. * @param lastVisit The date of the last visit.
*/ */
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) { public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) {
this.domain = domain; this.domain = domain;
@ -987,7 +1011,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* *
* @param programName The name of the program. * @param programName The name of the program.
* @param programPath The path of the program. * @param programPath The path of the program.
* @param runTimes The number of runs. * @param runTimes The number of runs.
*/ */
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
this.programName = programName; this.programName = programName;

View File

@ -1,3 +1,4 @@
ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}. ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
ILeappAnalyzerIngestModule.processing.file=Processing file {0} ILeappAnalyzerIngestModule.processing.file=Processing file {0}
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0} ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem

View File

@ -8,6 +8,7 @@ ILeappAnalyzerIngestModule.iLeapp.cancelled=iLeapp run was canceled
ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}. ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
ILeappAnalyzerIngestModule.processing.file=Processing file {0} ILeappAnalyzerIngestModule.processing.file=Processing file {0}
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0} ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows. ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp

View File

@ -18,8 +18,10 @@
*/ */
package org.sleuthkit.autopsy.modules.ileappanalyzer; package org.sleuthkit.autopsy.modules.ileappanalyzer;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -32,14 +34,17 @@ import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator; import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase; import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
@ -50,6 +55,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -61,7 +67,9 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName(); private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName();
private static final String ILEAPP = "iLeapp"; //NON-NLS private static final String ILEAPP = "iLeapp"; //NON-NLS
private static final String ILEAPP_FS = "fs_"; //NON-NLS
private static final String ILEAPP_EXECUTABLE = "ileapp.exe";//NON-NLS private static final String ILEAPP_EXECUTABLE = "ileapp.exe";//NON-NLS
private static final String ILEAPP_PATHS_FILE = "iLeapp_paths.txt"; //NON-NLS
private File iLeappExecutable; private File iLeappExecutable;
@ -87,7 +95,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
try { try {
iLeappFileProcessor = new ILeappFileProcessor(); iLeappFileProcessor = new ILeappFileProcessor();
} catch (IOException | IngestModuleException ex) { } catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex); throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
} }
@ -112,58 +120,49 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
@Override @Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
if (!(context.getDataSource() instanceof LocalFilesDataSource)) { Case currentCase = Case.getCurrentCase();
return ProcessResult.OK; Path tempOutputPath = Paths.get(currentCase.getTempDirectory(), ILEAPP, ILEAPP_FS + dataSource.getId());
try {
Files.createDirectories(tempOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", tempOutputPath.toString()), ex);
return ProcessResult.ERROR;
}
List<String> iLeappPathsToProcess = new ArrayList<>();
ProcessBuilder iLeappCommand = buildiLeappListCommand(tempOutputPath);
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return ProcessResult.ERROR;
}
iLeappPathsToProcess = loadIleappPathFile(tempOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program getting file paths to search"), ex);
return ProcessResult.ERROR;
} }
statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_starting_iLeapp(), 0); statusHelper.progress(Bundle.ILeappAnalyzerIngestModule_starting_iLeapp(), 0);
List<AbstractFile> iLeappFilesToProcess = findiLeappFilesToProcess(dataSource); List<AbstractFile> iLeappFilesToProcess = new ArrayList<>();
statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); if (!(context.getDataSource() instanceof LocalFilesDataSource)) {
extractFilesFromImage(dataSource, iLeappPathsToProcess, tempOutputPath);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else {
iLeappFilesToProcess = findiLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
Integer filesProcessedCount = 0; Integer filesProcessedCount = 0;
for (AbstractFile iLeappFile : iLeappFilesToProcess) {
Case currentCase = Case.getCurrentCase(); processILeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, iLeappFile);
for (AbstractFile iLeappFile : iLeappFilesToProcess) { filesProcessedCount++;
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return ProcessResult.ERROR;
} }
// Process the logical image as a fs in iLeapp to make sure this is not a logical fs that was added
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.file", iLeappFile.getName()), filesProcessedCount); extractFilesFromImage(dataSource, iLeappPathsToProcess, tempOutputPath);
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension()); processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
// ignore if there is an error and continue to try and process the next file if there is one
continue;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex);
return ProcessResult.ERROR;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return ProcessResult.OK;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile);
if (fileProcessorResult == ProcessResult.ERROR) {
return ProcessResult.ERROR;
}
filesProcessedCount++;
} }
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
@ -173,6 +172,99 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
return ProcessResult.OK; return ProcessResult.OK;
} }
/**
* Process each tar/zip file that is found in a logical image that contains xLeapp data
* @param dataSource Datasource where the file has been found
* @param currentCase current case
* @param statusHelper Progress bar for messages to show user
* @param filesProcessedCount count that is incremented for progress bar
* @param iLeappFile abstract file that will be processed
*/
private void processILeappFile(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, int filesProcessedCount,
AbstractFile iLeappFile) {
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return;
}
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.file", iLeappFile.getName()), filesProcessedCount);
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, iLeappFile.getLocalAbsPath(), iLeappFile.getNameExtension());
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file %s", iLeappFile.getLocalAbsPath()), ex);
return;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile);
if (fileProcessorResult == ProcessResult.ERROR) {
return;
}
}
/**
* Process extracted files from a disk image using xLeapp
* @param dataSource Datasource where the file has been found
* @param currentCase current case
* @param statusHelper Progress bar for messages to show user
* @param directoryToProcess
*/
private void processILeappFs(Content dataSource, Case currentCase, DataSourceIngestModuleProgress statusHelper, String directoryToProcess) {
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), ILEAPP, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error creating iLeapp output directory %s", moduleOutputPath.toString()), ex);
return;
}
statusHelper.progress(NbBundle.getMessage(this.getClass(), "ILeappAnalyzerIngestModule.processing.filesystem"));
ProcessBuilder iLeappCommand = buildiLeappCommand(moduleOutputPath, directoryToProcess, "fs");
try {
int result = ExecUtil.execute(iLeappCommand, new DataSourceIngestModuleProcessTerminator(context, true));
if (result != 0) {
logger.log(Level.WARNING, String.format("Error when trying to execute iLeapp program getting file paths to search for result is %d", result));
return;
}
addILeappReportToReports(moduleOutputPath, currentCase);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error when trying to execute iLeapp program against file system"), ex);
return;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
return;
}
ProcessResult fileProcessorResult = iLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
if (fileProcessorResult == ProcessResult.ERROR) {
return;
}
}
/** /**
* Find the files that will be processed by the iLeapp program * Find the files that will be processed by the iLeapp program
* *
@ -197,17 +289,24 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
List<AbstractFile> iLeappFilesToProcess = new ArrayList<>(); List<AbstractFile> iLeappFilesToProcess = new ArrayList<>();
for (AbstractFile iLeappFile : iLeappFiles) { for (AbstractFile iLeappFile : iLeappFiles) {
if (((iLeappFile.getLocalAbsPath() != null) if (((iLeappFile.getLocalAbsPath() != null)
&& (!iLeappFile.getNameExtension().isEmpty() && (!iLeappFile.isVirtual()))) && (!iLeappFile.getNameExtension().isEmpty() && (!iLeappFile.isVirtual())))
&& ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar"))) && ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")))
|| iLeappFile.getName().toLowerCase().contains(".tgz"))) { || iLeappFile.getName().toLowerCase().contains(".tgz"))) {
iLeappFilesToProcess.add(iLeappFile); iLeappFilesToProcess.add(iLeappFile);
} }
} }
return iLeappFilesToProcess; return iLeappFilesToProcess;
} }
/**
* Build the command to run xLeapp
* @param moduleOutputPath output path for xLeapp
* @param sourceFilePath path where the xLeapp file is
* @param iLeappFileSystem type of file to process tar/zip/fs
* @return process to run
*/
private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) { private ProcessBuilder buildiLeappCommand(Path moduleOutputPath, String sourceFilePath, String iLeappFileSystemType) {
ProcessBuilder processBuilder = buildProcessWithRunAsInvoker( ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
@ -221,10 +320,26 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
return processBuilder; return processBuilder;
} }
/**
* Command to run xLeapp using the path option
* @param moduleOutputPath path where the file paths output will reside
* @return process to run
*/
private ProcessBuilder buildiLeappListCommand(Path moduleOutputPath) {
ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
"\"" + iLeappExecutable + "\"", //NON-NLS
"-p"
);
processBuilder.redirectError(moduleOutputPath.resolve("iLeapp_paths_error.txt").toFile()); //NON-NLS
processBuilder.redirectOutput(moduleOutputPath.resolve("iLeapp_paths.txt").toFile()); //NON-NLS
return processBuilder;
}
static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
ProcessBuilder processBuilder = new ProcessBuilder(commandLine); ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
/* /*
* Add an environment variable to force log2timeline/psort to run with * Add an environment variable to force iLeapp to run with
* the same permissions Autopsy uses. * the same permissions Autopsy uses.
*/ */
processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
@ -248,13 +363,18 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
private void addILeappReportToReports(Path iLeappOutputDir, Case currentCase) { private void addILeappReportToReports(Path iLeappOutputDir, Case currentCase) {
List<String> allIndexFiles = new ArrayList<>(); List<String> allIndexFiles = new ArrayList<>();
try (Stream<Path> walk = Files.walk(iLeappOutputDir)) { try (Stream<Path> walk = Files.walk(iLeappOutputDir)) {
allIndexFiles = walk.map(x -> x.toString()) allIndexFiles = walk.map(x -> x.toString())
.filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList()); .filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
if (!allIndexFiles.isEmpty()) { if (!allIndexFiles.isEmpty()) {
currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name()); // Check for existance of directory that holds report data if does not exist then report contains no data
String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
if (dataFilesDir.exists()) {
currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name());
}
} }
} catch (IOException | UncheckedIOException | TskCoreException ex) { } catch (IOException | UncheckedIOException | TskCoreException ex) {
@ -264,4 +384,129 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
} }
/*
* Reads the iLeapp paths file to get the paths that we want to extract
*
* @param moduleOutputPath path where the file paths output will reside
*/
private List<String> loadIleappPathFile(Path moduleOutputPath) throws FileNotFoundException, IOException {
List<String> iLeappPathsToProcess = new ArrayList<>();
Path filePath = Paths.get(moduleOutputPath.toString(), ILEAPP_PATHS_FILE);
try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toString()))) {
String line = reader.readLine();
while (line != null) {
if (line.contains("path list generation") || line.length() < 2) {
line = reader.readLine();
continue;
}
iLeappPathsToProcess.add(line.trim());
line = reader.readLine();
}
}
return iLeappPathsToProcess;
}
/**
* Extract files from a disk image to process with xLeapp
* @param dataSource Datasource of the image
* @param iLeappPathsToProcess List of paths to extract content from
* @param moduleOutputPath path to write content to
*/
private void extractFilesFromImage(Content dataSource, List<String> iLeappPathsToProcess, Path moduleOutputPath) {
FileManager fileManager = getCurrentCase().getServices().getFileManager();
for (String fullFilePath : iLeappPathsToProcess) {
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "ILeapp Analyser ingest module run was canceled"); //NON-NLS
break;
}
String ffp = fullFilePath.replaceAll("\\*", "%");
ffp = FilenameUtils.normalize(ffp, true);
String fileName = FilenameUtils.getName(ffp);
String filePath = FilenameUtils.getPath(ffp);
List<AbstractFile> iLeappFiles = new ArrayList<>();
try {
if (filePath.isEmpty()) {
iLeappFiles = fileManager.findFiles(dataSource, fileName); //NON-NLS
} else {
iLeappFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return;
}
for (AbstractFile iLeappFile : iLeappFiles) {
Path parentPath = Paths.get(moduleOutputPath.toString(), iLeappFile.getParentPath());
File fileParentPath = new File(parentPath.toString());
extractFileToOutput(dataSource, iLeappFile, fileParentPath, parentPath);
}
}
}
/**
* Create path and file from datasource in temp
* @param dataSource datasource of the image
* @param iLeappFile abstract file to write out
* @param fileParentPath parent file path
* @param parentPath parent file
*/
private void extractFileToOutput(Content dataSource, AbstractFile iLeappFile, File fileParentPath, Path parentPath) {
if (fileParentPath.exists()) {
if (!iLeappFile.isDir()) {
writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
} else {
try {
Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
}
} else {
try {
Files.createDirectories(parentPath);
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
if (!iLeappFile.isDir()) {
writeiLeappFile(dataSource, iLeappFile, fileParentPath.toString());
} else {
try {
Files.createDirectories(Paths.get(parentPath.toString(), iLeappFile.getName()));
} catch (IOException ex) {
logger.log(Level.INFO, String.format("Error creating iLeapp output directory %s", parentPath.toString()), ex);
}
}
}
}
/**
* Write out file to output
* @param dataSource datasource of disk image
* @param iLeappFile acstract file to write out
* @param parentPath path to write file to
*/
private void writeiLeappFile(Content dataSource, AbstractFile iLeappFile, String parentPath) {
String fileName = iLeappFile.getName().replace(":", "-");
if (!fileName.matches(".") && !fileName.matches("..") && !fileName.toLowerCase().endsWith("-slack")) {
Path filePath = Paths.get(parentPath, fileName);
File localFile = new File(filePath.toString());
try {
ContentUtils.writeToFile(iLeappFile, localFile, context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStream.ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d).",
iLeappFile.getName(), iLeappFile.getId()), ex); //NON-NLS
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error writing file local file '%s' (id=%d).",
filePath.toString(), iLeappFile.getId()), ex); //NON-NLS
}
}
}
} }

View File

@ -44,6 +44,7 @@ import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
@ -76,12 +77,16 @@ public final class ILeappFileProcessor {
private final Map<String, String> tsvFileArtifactComments; private final Map<String, String> tsvFileArtifactComments;
private final Map<String, List<List<String>>> tsvFileAttributes; private final Map<String, List<List<String>>> tsvFileAttributes;
public ILeappFileProcessor() throws IOException, IngestModuleException { Blackboard blkBoard;
public ILeappFileProcessor() throws IOException, IngestModuleException, NoCurrentCaseException {
this.tsvFiles = new HashMap<>(); this.tsvFiles = new HashMap<>();
this.tsvFileArtifacts = new HashMap<>(); this.tsvFileArtifacts = new HashMap<>();
this.tsvFileArtifactComments = new HashMap<>(); this.tsvFileArtifactComments = new HashMap<>();
this.tsvFileAttributes = new HashMap<>(); this.tsvFileAttributes = new HashMap<>();
blkBoard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
configExtractor(); configExtractor();
loadConfigFile(); loadConfigFile();
@ -110,6 +115,19 @@ public final class ILeappFileProcessor {
return ProcessResult.OK; return ProcessResult.OK;
} }
public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath) {
try {
List<String> iLeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processiLeappFiles(iLeappTsvOutputFiles, dataSource);
} catch (IOException | IngestModuleException ex) {
logger.log(Level.SEVERE, String.format("Error trying to process iLeapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
return ProcessResult.ERROR;
}
return ProcessResult.OK;
}
/** /**
* Find the tsv files in the iLeapp output directory and match them to files * Find the tsv files in the iLeapp output directory and match them to files
* we know we want to process and return the list to process those files. * we know we want to process and return the list to process those files.
@ -124,7 +142,7 @@ public final class ILeappFileProcessor {
.filter(f -> f.toLowerCase().endsWith(".tsv")).collect(Collectors.toList()); .filter(f -> f.toLowerCase().endsWith(".tsv")).collect(Collectors.toList());
for (String tsvFile : allTsvFiles) { for (String tsvFile : allTsvFiles) {
if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile))) { if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile.toLowerCase()))) {
foundTsvFiles.add(tsvFile); foundTsvFiles.add(tsvFile);
} }
} }
@ -160,7 +178,41 @@ public final class ILeappFileProcessor {
processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, iLeappImageFile); processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, iLeappImageFile);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
// check this throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
}
}
}
if (!bbartifacts.isEmpty()) {
postArtifacts(bbartifacts);
}
}
/**
* Process the iLeapp files that were found that match the xml mapping file
*
* @param iLeappFilesToProcess List of files to process
* @param iLeappImageFile Abstract file to create artifact for
*
* @throws FileNotFoundException
* @throws IOException
*/
private void processiLeappFiles(List<String> iLeappFilesToProcess, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException {
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
for (String iLeappFileName : iLeappFilesToProcess) {
String fileName = FilenameUtils.getName(iLeappFileName);
File iLeappFile = new File(iLeappFileName);
if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName);
try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex); throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
} }
} }
@ -174,7 +226,8 @@ public final class ILeappFileProcessor {
} }
private void processFile(File iLeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType, private void processFile(File iLeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType,
List<BlackboardArtifact> bbartifacts, AbstractFile iLeappImageFile) throws FileNotFoundException, IOException, IngestModuleException { List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
TskCoreException {
try (BufferedReader reader = new BufferedReader(new FileReader(iLeappFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(iLeappFile))) {
String line = reader.readLine(); String line = reader.readLine();
// Check first line, if it is null then no heading so nothing to match to, close and go to next file. // Check first line, if it is null then no heading so nothing to match to, close and go to next file.
@ -183,8 +236,8 @@ public final class ILeappFileProcessor {
line = reader.readLine(); line = reader.readLine();
while (line != null) { while (line != null) {
Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName); Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName);
if (!bbattributes.isEmpty()) { if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), iLeappImageFile, bbattributes); BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
if (bbartifact != null) { if (bbartifact != null) {
bbartifacts.add(bbartifact); bbartifacts.add(bbartifact);
} }
@ -234,8 +287,8 @@ public final class ILeappFileProcessor {
} }
private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, Integer columnNumber, BlackboardAttribute.Type attributeType, private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, Integer columnNumber, BlackboardAttribute.Type attributeType,
String fileName) { String fileName) {
if (attrType.matches("STRING")) { if (attrType.matches("STRING")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber])); bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber]));
} else if (attrType.matches("INTEGER")) { } else if (attrType.matches("INTEGER")) {
@ -340,7 +393,7 @@ public final class ILeappFileProcessor {
for (int i = 0; i < nlist.getLength(); i++) { for (int i = 0; i < nlist.getLength(); i++) {
NamedNodeMap nnm = nlist.item(i).getAttributes(); NamedNodeMap nnm = nlist.item(i).getAttributes();
tsvFiles.put(nnm.getNamedItem("filename").getNodeValue(), nnm.getNamedItem("description").getNodeValue()); tsvFiles.put(nnm.getNamedItem("filename").getNodeValue().toLowerCase(), nnm.getNamedItem("description").getNodeValue());
} }
@ -393,19 +446,20 @@ public final class ILeappFileProcessor {
} }
} }
/**
* Generic method for creating a blackboard artifact with attributes /**
* * Generic method for creating a blackboard artifact with attributes
* @param type is a blackboard.artifact_type enum to determine *
* which type the artifact should be * @param type is a blackboard.artifact_type enum to determine which
* @param abstractFile is the AbstractFile object that needs to have the * type the artifact should be
* artifact added for it * @param abstractFile is the AbstractFile object that needs to have the
* @param bbattributes is the collection of blackboard attributes that * artifact added for it
* need to be added to the artifact after the * @param bbattributes is the collection of blackboard attributes that need
* artifact has been created * to be added to the artifact after the artifact has
* * been created
* @return The newly-created artifact, or null on error *
*/ * @return The newly-created artifact, or null on error
*/
private BlackboardArtifact createArtifactWithAttributes(int type, AbstractFile abstractFile, Collection<BlackboardAttribute> bbattributes) { private BlackboardArtifact createArtifactWithAttributes(int type, AbstractFile abstractFile, Collection<BlackboardAttribute> bbattributes) {
try { try {
BlackboardArtifact bbart = abstractFile.newArtifact(type); BlackboardArtifact bbart = abstractFile.newArtifact(type);
@ -417,6 +471,30 @@ public final class ILeappFileProcessor {
return null; return null;
} }
/**
* Generic method for creating a blackboard artifact with attributes
*
* @param type is a blackboard.artifact_type enum to determine which
* type the artifact should be
* @param datasource is the Content object that needs to have the artifact
* added for it
* @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has
* been created
*
* @return The newly-created artifact, or null on error
*/
private BlackboardArtifact createArtifactWithAttributes(int type, Content dataSource, Collection<BlackboardAttribute> bbattributes) {
try {
BlackboardArtifact bbart = dataSource.newArtifact(type);
bbart.addAttributes(bbattributes);
return bbart;
} catch (TskException ex) {
logger.log(Level.WARNING, Bundle.ILeappFileProcessor_error_creating_new_artifacts(), ex); //NON-NLS
}
return null;
}
/** /**
* Method to post a list of BlackboardArtifacts to the blackboard. * Method to post a list of BlackboardArtifacts to the blackboard.
* *

View File

@ -47,6 +47,15 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="App Snapshots.tsv" description="App Snapshots (screenshots)">
<ArtifactName artifactname="TSK_SCREEN_SHOTS" comment="null">
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="TSK_PATH" columnName="SOurce Path" required="yes" />
<AttributeName attributename="TSK_DATETIME" columnName="Date Modified" required="yes" />
<AttributeName attributename="null" columnName="Source File Located" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Bluetooth Other.tsv" description="Bluetooth Other"> <FileName filename="Bluetooth Other.tsv" description="Bluetooth Other">
<ArtifactName artifactname="TSK_BLUETOOTH_ADAPTER" comment="Bluetooth Other"> <ArtifactName artifactname="TSK_BLUETOOTH_ADAPTER" comment="Bluetooth Other">
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
@ -120,6 +129,13 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="DHCP Received List.tsv" description="DHCP Received List" >
<ArtifactName artifactname="TSK_IP_DHCP" comment="null">
<AttributeName attributename="TSK_NAME" columnName="Key" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC App Activity.tsv" description="KnowledgeC App Activity"> <FileName filename="KnowledgeC App Activity.tsv" description="KnowledgeC App Activity">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Activity"> <ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Activity">
<AttributeName attributename="TSK_DATETIME" columnName="Entry Creation" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Entry Creation" required="yes" />
@ -189,6 +205,36 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Device is Backlit.tsv" description="KnowledgeC Device is Backlit">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Screen is Backlit" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Battery Level.tsv" description="KnowledgeC Battery Level">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Battery Level" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Bluetooth Connections.tsv" description="KnowledgeC Bluetooth Connections"> <FileName filename="KnowledgeC Bluetooth Connections.tsv" description="KnowledgeC Bluetooth Connections">
<ArtifactName artifactname="TSK_BLUETOOTH_PAIRING" comment="KnowledgeC Bluetooth Connections"> <ArtifactName artifactname="TSK_BLUETOOTH_PAIRING" comment="KnowledgeC Bluetooth Connections">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
@ -207,15 +253,61 @@
<FileName filename="KnowledgeC Car Play Connections.tsv" description="KnowledgeC Car Play Connections"> <FileName filename="KnowledgeC Car Play Connections.tsv" description="KnowledgeC Car Play Connections">
<ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections"> <ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="no" /> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
<AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Car Play Connected" required="no" /> <AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Car Play Connected" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="TSK_DEVICE_ID" columnName="UUID" required="no" /> <AttributeName attributename="TSK_DEVICE_ID" columnName="UUID" required="yes" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Disk Subsystem Access.tsv" description="KnowledgeC Disk Subsystem Access">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="disk Subsystem">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value String" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Do Not Disturb.tsv" description="KnowledgeC Do Not Disturb">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Do Not Disturb">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Inferred Motion.tsv" description="KnowledgeC Inferred Motion">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" /> <AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -248,6 +340,19 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Device Locked.tsv" description="KnowledgeC Device Locked">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Locked?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Media Playing.tsv" description="KnowledgeC Media Playing"> <FileName filename="Media Playing.tsv" description="KnowledgeC Media Playing">
<ArtifactName artifactname="TSK_RECENT_OBJ" comment="KnowledgeC Media Playing"> <ArtifactName artifactname="TSK_RECENT_OBJ" comment="KnowledgeC Media Playing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -288,6 +393,36 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Screen Orientation.tsv" description="KnowledgeC Screen Orientation">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Orientation" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Plugged In.tsv" description="KnowledgeC Plugged In">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Plugged In?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Safari Browsing.tsv" description="KnowledgeC Safari Browsing"> <FileName filename="KnowledgeC Safari Browsing.tsv" description="KnowledgeC Safari Browsing">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Safari Browsing"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Safari Browsing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -302,6 +437,18 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC Siri Usage.tsv" description="KnowledgeC Siri Usage">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Siri Usage">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="Weekday" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC App Usage.tsv" description="KnowledgeC App Usage"> <FileName filename="KnowledgeC App Usage.tsv" description="KnowledgeC App Usage">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Usage"> <ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Usage">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
@ -317,6 +464,18 @@
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" /> <AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="KnowledgeC User Waking Events.tsv" description="KnowledgeC User Waking Event">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="User Waking">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="Zobject Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="KnowledgeC Web Usage.tsv" description="KnowledgeC Web Usage"> <FileName filename="KnowledgeC Web Usage.tsv" description="KnowledgeC Web Usage">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Web Usage"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Web Usage">
@ -433,6 +592,102 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
--> -->
<FileName filename="Notifications.tsv" description="iOS Notificatons">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons">
<AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Bundle" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title[Subtitle]" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName=" Message" required="yes" />
<AttributeName attributename="null" columnName=" Other Details" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Agg Bulletins.tsv" description="Powerlog Aggregate Bulletins">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="Powerlog Aggregate Bulletins">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bulletin Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Time Interval in Seconds" required="no" />
<AttributeName attributename="null" columnName="Count" required="no" />
<AttributeName attributename="null" columnName="Post Type" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Agg Notifications.tsv" description="Powerlog Aggregate Notifications">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="Powerlog Aggregate Notifications">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Notification Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Time Interval in Seconds" required="no" />
<AttributeName attributename="null" columnName="Count" required="no" />
<AttributeName attributename="null" columnName="Notification Type" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Backup Info.tsv" description="Powerlog Backup Info">
<ArtifactName artifactname="TSK_BACKUP_EVENT" comment="null">
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="null" columnName="State" required="no" />
<AttributeName attributename="null" columnName="Finished" required="no" />
<AttributeName attributename="null" columnName="Has error" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Deleted Apps.tsv" description="Powerlog Deleted Apps">
<ArtifactName artifactname="TSK_DELETED_PROG" comment="Powerlog Deleted Apps">
<AttributeName attributename="TSK_DATETIME_DELETED" columnName="App Deleted Date" required="yes" />
<AttributeName attributename="TSK_DATETIME" columnName="Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="App Executable Name" required="no" />
<AttributeName attributename="TSK_PATH" columnName="Bundle ID" required="yes" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Lightning Connector.tsv" description="Powerlog Lightning Connector Status">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Lightning Connector Status">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Accesory Power Mode" required="yes" />
<AttributeName attributename="null" columnName="Original Lightnint Connector Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Push Message Received.tsv" description="Powerlog Push Message Received">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Push Message Received">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Connection Type" required="yes" />
<AttributeName attributename="null" columnName="Is Dropped" required="no" />
<AttributeName attributename="null" columnName="Link Quality" required="no" />
<AttributeName attributename="null" columnName="Priority" required="no" />
<AttributeName attributename="null" columnName="Topic" required="no" />
<AttributeName attributename="null" columnName="Server Hostname" required="no" />
<AttributeName attributename="null" columnName="Server IP" required="no" />
<AttributeName attributename="null" columnName="Original Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" />
<AttributeName attributename="null" columnName="Aggregate Table ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Torch.tsv" description="Powerlog Torch">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Status" required="yes" />
<AttributeName attributename="null" columnName="Original Torch Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" />
<AttributeName attributename="null" columnName="Torch ID" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Powerlog Wifi Network Connections.tsv" description="Powerlog WiFi Network Connections"> <FileName filename="Powerlog Wifi Network Connections.tsv" description="Powerlog WiFi Network Connections">
<ArtifactName artifactname="TSK_WIFI_NETWORK" comment="Powerlog WiFi Network Connections"> <ArtifactName artifactname="TSK_WIFI_NETWORK" comment="Powerlog WiFi Network Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />

View File

@ -71,8 +71,8 @@ public class RecentFilesSummaryTest {
* Means of acquiring data from a method in RecentFilesSummary. * Means of acquiring data from a method in RecentFilesSummary.
* *
* @param recentFilesSummary The RecentFilesSummary object. * @param recentFilesSummary The RecentFilesSummary object.
* @param dataSource The datasource. * @param dataSource The datasource.
* @param count The number of items to retrieve. * @param count The number of items to retrieve.
* *
* @return The method's return data. * @return The method's return data.
* *
@ -95,7 +95,7 @@ public class RecentFilesSummaryTest {
/** /**
* If -1 count passed to method, should throw IllegalArgumentException. * If -1 count passed to method, should throw IllegalArgumentException.
* *
* @param method The method to call. * @param method The method to call.
* @param methodName The name of the metho * @param methodName The name of the metho
* *
* @throws TskCoreException * @throws TskCoreException
@ -137,7 +137,7 @@ public class RecentFilesSummaryTest {
* SleuthkitCase isn't called. * SleuthkitCase isn't called.
* *
* @param recentFilesMethod The method to call. * @param recentFilesMethod The method to call.
* @param methodName The name of the method * @param methodName The name of the method
* *
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
@ -175,7 +175,7 @@ public class RecentFilesSummaryTest {
* If SleuthkitCase returns no results, an empty list is returned. * If SleuthkitCase returns no results, an empty list is returned.
* *
* @param recentFilesMethod The method to call. * @param recentFilesMethod The method to call.
* @param methodName The name of the method. * @param methodName The name of the method.
* *
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
@ -220,11 +220,11 @@ public class RecentFilesSummaryTest {
/** /**
* Gets a mock BlackboardArtifact. * Gets a mock BlackboardArtifact.
* *
* @param ds The data source to which the artifact belongs. * @param ds The data source to which the artifact belongs.
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param artType The artifact type. * @param artType The artifact type.
* @param attributeArgs The mapping of attribute type to value for each * @param attributeArgs The mapping of attribute type to value for each
* attribute in the artifact. * attribute in the artifact.
* *
* @return The mock artifact. * @return The mock artifact.
*/ */
@ -247,10 +247,10 @@ public class RecentFilesSummaryTest {
/** /**
* Returns a mock artifact for getRecentlyOpenedDocuments. * Returns a mock artifact for getRecentlyOpenedDocuments.
* *
* @param ds The datasource for the artifact. * @param ds The datasource for the artifact.
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param dateTime The time in seconds from epoch. * @param dateTime The time in seconds from epoch.
* @param path The path for the document. * @param path The path for the document.
* *
* @return The mock artifact with pertinent attributes. * @return The mock artifact with pertinent attributes.
*/ */
@ -292,13 +292,33 @@ public class RecentFilesSummaryTest {
} }
} }
@Test
public void getRecentlyOpenedDocuments_uniquePaths() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1);
BlackboardArtifact item1 = getRecentDocumentArtifact(dataSource, 1001, DAY_SECONDS, "/a/path");
BlackboardArtifact item2 = getRecentDocumentArtifact(dataSource, 1002, DAY_SECONDS + 1, "/a/path");
BlackboardArtifact item3 = getRecentDocumentArtifact(dataSource, 1003, DAY_SECONDS + 2, "/a/path");
List<BlackboardArtifact> artifacts = Arrays.asList(item2, item3, item1);
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
List<RecentFileDetails> results = summary.getRecentlyOpenedDocuments(dataSource, 10);
// verify results (only successItem)
Assert.assertNotNull(results);
Assert.assertEquals(1, results.size());
Assert.assertEquals((Long) (DAY_SECONDS + 2), results.get(0).getDateAsLong());
Assert.assertTrue("/a/path".equalsIgnoreCase(results.get(0).getPath()));
}
@Test @Test
public void getRecentlyOpenedDocuments_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException { public void getRecentlyOpenedDocuments_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1); DataSource dataSource = TskMockUtils.getDataSource(1);
BlackboardArtifact successItem = getRecentDocumentArtifact(dataSource, 1001, DAY_SECONDS, "/a/path"); BlackboardArtifact successItem = getRecentDocumentArtifact(dataSource, 1001, DAY_SECONDS, "/a/path");
BlackboardArtifact nullTime = getRecentDocumentArtifact(dataSource, 1002, null, "/a/path2"); BlackboardArtifact nullTime = getRecentDocumentArtifact(dataSource, 1002, null, "/a/path2");
BlackboardArtifact zeroTime = getRecentDocumentArtifact(dataSource, 10021, 0L, "/a/path2a"); BlackboardArtifact zeroTime = getRecentDocumentArtifact(dataSource, 10021, 0L, "/a/path2a");
List<BlackboardArtifact> artifacts = Arrays.asList(nullTime, zeroTime, successItem); List<BlackboardArtifact> artifacts = Arrays.asList(nullTime, zeroTime, successItem);
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts)); Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
@ -315,11 +335,11 @@ public class RecentFilesSummaryTest {
/** /**
* Creates a mock blackboard artifact for getRecentDownloads. * Creates a mock blackboard artifact for getRecentDownloads.
* *
* @param ds The datasource. * @param ds The datasource.
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param dateTime The time in seconds from epoch. * @param dateTime The time in seconds from epoch.
* @param domain The domain. * @param domain The domain.
* @param path The path for the download. * @param path The path for the download.
* *
* @return The mock artifact. * @return The mock artifact.
*/ */
@ -368,6 +388,30 @@ public class RecentFilesSummaryTest {
} }
} }
@Test
public void getRecentDownloads_uniquePaths() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1);
BlackboardArtifact item1 = getRecentDownloadArtifact(dataSource, 1001, DAY_SECONDS, "domain1.com", "/a/path1");
BlackboardArtifact item1a = getRecentDownloadArtifact(dataSource, 10011, DAY_SECONDS + 1, "domain1.com", "/a/path1");
BlackboardArtifact item2 = getRecentDownloadArtifact(dataSource, 1002, DAY_SECONDS + 2, "domain2.com", "/a/path1");
BlackboardArtifact item3 = getRecentDownloadArtifact(dataSource, 1003, DAY_SECONDS + 3, "domain2a.com", "/a/path1");
List<BlackboardArtifact> artifacts = Arrays.asList(item2, item3, item1);
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
// call method
List<RecentDownloadDetails> results = summary.getRecentDownloads(dataSource, 10);
// verify results
Assert.assertNotNull(results);
Assert.assertEquals(1, results.size());
Assert.assertEquals((Long) (DAY_SECONDS + 3), results.get(0).getDateAsLong());
Assert.assertTrue("/a/path1".equalsIgnoreCase(results.get(0).getPath()));
Assert.assertTrue("domain2a.com".equalsIgnoreCase(results.get(0).getWebDomain()));
}
@Test @Test
public void getRecentDownloads_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException { public void getRecentDownloads_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1); DataSource dataSource = TskMockUtils.getDataSource(1);
@ -409,19 +453,17 @@ public class RecentFilesSummaryTest {
* Constructor with all parameters. * Constructor with all parameters.
* *
* @param messageArtifactTypeId The type id for the artifact or null if * @param messageArtifactTypeId The type id for the artifact or null if
* no message artifact to be created. * no message artifact to be created.
* @param emailFrom Who the message is from or null not to * @param emailFrom Who the message is from or null not to include
* include attribute. * attribute.
* @param messageTime Time in seconds from epoch or null not * @param messageTime Time in seconds from epoch or null not to include
* to include attribute. * attribute.
* @param fileParentPath The parent AbstractFile's path value. * @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename * @param fileName The parent AbstractFile's filename value.
* value. * @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT
* @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT * artifact has no attribute (even though it is required).
* artifact has no attribute (even though * @param hasParent Whether or not the artifact has a parent
* it is required). * AbstractFile.
* @param hasParent Whether or not the artifact has a parent
* AbstractFile.
*/ */
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime, AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime,
String fileParentPath, String fileName, String fileParentPath, String fileName,
@ -441,14 +483,13 @@ public class RecentFilesSummaryTest {
* SleuthkitCase assumed. * SleuthkitCase assumed.
* *
* @param messageArtifactTypeId The type id for the artifact or null if * @param messageArtifactTypeId The type id for the artifact or null if
* no message artifact to be created. * no message artifact to be created.
* @param emailFrom Who the message is from or null not to * @param emailFrom Who the message is from or null not to include
* include attribute. * attribute.
* @param messageTime Time in seconds from epoch or null not * @param messageTime Time in seconds from epoch or null not to include
* to include attribute. * attribute.
* @param fileParentPath The parent AbstractFile's path value. * @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename * @param fileName The parent AbstractFile's filename value.
* value.
*/ */
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime, String fileParentPath, String fileName) { AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime, String fileParentPath, String fileName) {
this(messageArtifactTypeId, emailFrom, messageTime, fileParentPath, fileName, true, true); this(messageArtifactTypeId, emailFrom, messageTime, fileParentPath, fileName, true, true);
@ -486,11 +527,11 @@ public class RecentFilesSummaryTest {
/** /**
* Sets up the associated artifact message for the TSK_ASSOCIATED_OBJECT. * Sets up the associated artifact message for the TSK_ASSOCIATED_OBJECT.
* *
* @param artifacts The mapping of artifact id to artifact. * @param artifacts The mapping of artifact id to artifact.
* @param item The record to setup. * @param item The record to setup.
* @param dataSource The datasource. * @param dataSource The datasource.
* @param associatedId The associated attribute id. * @param associatedId The associated attribute id.
* @param artifactId The artifact id. * @param artifactId The artifact id.
* *
* @return The associated Artifact blackboard attribute. * @return The associated Artifact blackboard attribute.
* *
@ -504,7 +545,7 @@ public class RecentFilesSummaryTest {
if (item.getMessageArtifactTypeId() == null) { if (item.getMessageArtifactTypeId() == null) {
return associatedAttr; return associatedAttr;
} }
// find the artifact type or null if not found // find the artifact type or null if not found
ARTIFACT_TYPE messageType = Stream.of(ARTIFACT_TYPE.values()) ARTIFACT_TYPE messageType = Stream.of(ARTIFACT_TYPE.values())
.filter((artType) -> artType.getTypeID() == item.getMessageArtifactTypeId()) .filter((artType) -> artType.getTypeID() == item.getMessageArtifactTypeId())
@ -534,7 +575,7 @@ public class RecentFilesSummaryTest {
* to return pertinent data. * to return pertinent data.
* *
* @param items Each attachment item where each item could represent a * @param items Each attachment item where each item could represent a
* return result if fully formed. * return result if fully formed.
* *
* @return The mock SleuthkitCase and Blackboard. * @return The mock SleuthkitCase and Blackboard.
*/ */
@ -678,4 +719,34 @@ public class RecentFilesSummaryTest {
.toString().equalsIgnoreCase(successItem2Details.getPath())); .toString().equalsIgnoreCase(successItem2Details.getPath()));
Assert.assertTrue(successItem2.getEmailFrom().equalsIgnoreCase(successItem2Details.getSender())); Assert.assertTrue(successItem2.getEmailFrom().equalsIgnoreCase(successItem2Details.getSender()));
} }
@Test
public void getRecentAttachments_uniquePath() throws SleuthkitCaseProviderException, TskCoreException {
// setup data
DataSource dataSource = TskMockUtils.getDataSource(1);
AttachmentArtifactItem item1 = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
"person@sleuthkit.com", DAY_SECONDS, "/parent/path", "msg.pdf");
AttachmentArtifactItem item2 = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
"person_on_skype", DAY_SECONDS + 1, "/parent/path", "msg.pdf");
AttachmentArtifactItem item3 = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
"person2@sleuthkit.com", DAY_SECONDS + 2, "/parent/path", "msg.pdf");
List<AttachmentArtifactItem> items = Arrays.asList(item1, item2, item3);
Pair<SleuthkitCase, Blackboard> casePair = getRecentAttachmentArtifactCase(items);
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
// get data
List<RecentAttachmentDetails> results = summary.getRecentAttachments(dataSource, 10);
// verify results
Assert.assertNotNull(results);
Assert.assertEquals(1, results.size());
Assert.assertEquals(results.get(0).getDateAsLong(), (Long) (DAY_SECONDS + 2));
Assert.assertTrue(Paths.get(item3.getFileParentPath(), item3.getFileName())
.toString().equalsIgnoreCase(results.get(0).getPath()));
Assert.assertTrue(results.get(0).getSender().equalsIgnoreCase(item3.getEmailFrom()));
}
} }

View File

@ -66,18 +66,22 @@ import org.sleuthkit.datamodel.TskCoreException;
* Tests for UserActivitySummary. * Tests for UserActivitySummary.
*/ */
public class UserActivitySummaryTest { public class UserActivitySummaryTest {
/** /**
* Function to retrieve data from UserActivitySummary with the provided arguments. * Function to retrieve data from UserActivitySummary with the provided
* arguments.
*/ */
private interface DataFunction<T> { private interface DataFunction<T> {
/** /**
* A UserActivitySummary method encapsulated in a uniform manner. * A UserActivitySummary method encapsulated in a uniform manner.
*
* @param userActivitySummary The UserActivitySummary class to use. * @param userActivitySummary The UserActivitySummary class to use.
* @param datasource The data source. * @param datasource The data source.
* @param count The count. * @param count The count.
* @return The list of objects to return. * @return The list of objects to return.
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
*/ */
List<T> retrieve(UserActivitySummary userActivitySummary, DataSource datasource, int count) throws List<T> retrieve(UserActivitySummary userActivitySummary, DataSource datasource, int count) throws
SleuthkitCaseProviderException, TskCoreException; SleuthkitCaseProviderException, TskCoreException;
@ -117,8 +121,8 @@ public class UserActivitySummaryTest {
/** /**
* Gets a UserActivitySummary class to test. * Gets a UserActivitySummary class to test.
* *
* @param tskCase The SleuthkitCase. * @param tskCase The SleuthkitCase.
* @param hasTranslation Whether the translation service is functional. * @param hasTranslation Whether the translation service is functional.
* @param translateFunction Function for translation. * @param translateFunction Function for translation.
* *
* @return The UserActivitySummary class to use for testing. * @return The UserActivitySummary class to use for testing.
@ -333,6 +337,28 @@ public class UserActivitySummaryTest {
} }
} }
@Test
public void getRecentDevices_uniqueByDeviceId()
throws TskCoreException, NoServiceProviderException, SleuthkitCaseProviderException, TskCoreException, TranslationException {
long dataSourceId = 1L;
DataSource dataSource = TskMockUtils.getDataSource(dataSourceId);
BlackboardArtifact item1 = getRecentDeviceArtifact(1001, dataSource, "ID1", "MAKE1", "MODEL1", DAY_SECONDS);
BlackboardArtifact item2 = getRecentDeviceArtifact(1002, dataSource, "ID1", "MAKE1", "MODEL1", DAY_SECONDS + 1);
BlackboardArtifact item3 = getRecentDeviceArtifact(1003, dataSource, "ID1", "MAKE1", "MODEL1", DAY_SECONDS + 2);
Pair<SleuthkitCase, Blackboard> tskPair = getArtifactsTSKMock(Arrays.asList(item1, item2, item3));
UserActivitySummary summary = getTestClass(tskPair.getLeft(), false, null);
List<TopDeviceAttachedResult> results = summary.getRecentDevices(dataSource, 10);
Assert.assertEquals(1, results.size());
Assert.assertEquals((long) (DAY_SECONDS + 2), results.get(0).getDateAccessed().getTime() / 1000);
Assert.assertTrue("ID1".equalsIgnoreCase(results.get(0).getDeviceId()));
Assert.assertTrue("MAKE1".equalsIgnoreCase(results.get(0).getDeviceMake()));
Assert.assertTrue("MODEL1".equalsIgnoreCase(results.get(0).getDeviceModel()));
}
private static BlackboardArtifact getWebSearchArtifact(long artifactId, DataSource dataSource, String query, Long date) { private static BlackboardArtifact getWebSearchArtifact(long artifactId, DataSource dataSource, String query, Long date) {
try { try {
return TskMockUtils.getArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY), artifactId, dataSource, return TskMockUtils.getArtifact(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY), artifactId, dataSource,
@ -708,8 +734,8 @@ public class UserActivitySummaryTest {
* *
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param dataSource The datasource. * @param dataSource The datasource.
* @param dateRcvd The date received in seconds or null to exclude. * @param dateRcvd The date received in seconds or null to exclude.
* @param dateSent The date sent in seconds or null to exclude. * @param dateSent The date sent in seconds or null to exclude.
* *
* @return The mock artifact. * @return The mock artifact.
*/ */
@ -738,8 +764,8 @@ public class UserActivitySummaryTest {
* *
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param dataSource The datasource. * @param dataSource The datasource.
* @param dateStart The date start in seconds or null to exclude. * @param dateStart The date start in seconds or null to exclude.
* @param dateEnd The date end in seconds or null to exclude. * @param dateEnd The date end in seconds or null to exclude.
* *
* @return The mock artifact. * @return The mock artifact.
*/ */
@ -768,8 +794,8 @@ public class UserActivitySummaryTest {
* *
* @param artifactId The artifact id. * @param artifactId The artifact id.
* @param dataSource The datasource. * @param dataSource The datasource.
* @param type The account type. * @param type The account type.
* @param dateSent The date of the message in seconds. * @param dateSent The date of the message in seconds.
*/ */
private static BlackboardArtifact getMessageArtifact(long artifactId, DataSource dataSource, String type, Long dateTime) { private static BlackboardArtifact getMessageArtifact(long artifactId, DataSource dataSource, String type, Long dateTime) {
List<BlackboardAttribute> attributes = new ArrayList<>(); List<BlackboardAttribute> attributes = new ArrayList<>();
@ -794,11 +820,11 @@ public class UserActivitySummaryTest {
/** /**
* Performs a test on UserActivitySummary.getRecentAccounts. * Performs a test on UserActivitySummary.getRecentAccounts.
* *
* @param dataSource The datasource to use as parameter. * @param dataSource The datasource to use as parameter.
* @param count The count to use as a parameter. * @param count The count to use as a parameter.
* @param retArtifacts The artifacts to return from * @param retArtifacts The artifacts to return from
* SleuthkitCase.getArtifacts. This method filters * SleuthkitCase.getArtifacts. This method filters based on artifact type
* based on artifact type from the call. * from the call.
* @param expectedResults The expected results. * @param expectedResults The expected results.
* *
* @throws TskCoreException * @throws TskCoreException

View File

@ -76,6 +76,10 @@
<sysproperty key="solrPort" value="${solrPort}"/> <sysproperty key="solrPort" value="${solrPort}"/>
<sysproperty key="messageServiceHost" value="${messageServiceHost}"/> <sysproperty key="messageServiceHost" value="${messageServiceHost}"/>
<sysproperty key="messageServicePort" value="${messageServicePort}"/> <sysproperty key="messageServicePort" value="${messageServicePort}"/>
<sysproperty key="crHost" value="${crHost}"/>
<sysproperty key="crPort" value="${crPort}"/>
<sysproperty key="crUserName" value="${crUserName}"/>
<sysproperty key="crPassword" value="${crPassword}"/>
<sysproperty key="isMultiUser" value="${isMultiUser}"/> <sysproperty key="isMultiUser" value="${isMultiUser}"/>
<!--needed to have tests NOT to steal focus when running, works in latest apple jdk update only.--> <!--needed to have tests NOT to steal focus when running, works in latest apple jdk update only.-->
<sysproperty key="apple.awt.UIElement" value="@{disable.apple.ui}"/> <sysproperty key="apple.awt.UIElement" value="@{disable.apple.ui}"/>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -25,6 +25,9 @@ import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.netbeans.jemmy.Timeouts; import org.netbeans.jemmy.Timeouts;
import org.netbeans.junit.NbModuleSuite; import org.netbeans.junit.NbModuleSuite;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
/** /**
* This test expects the following system properties to be set: img_path: The * This test expects the following system properties to be set: img_path: The
@ -100,6 +103,21 @@ public class RegressionTest extends TestCase {
public void setUp() { public void setUp() {
logger.info("######## " + AutopsyTestCases.getEscapedPath(System.getProperty("img_path")) + " #######"); logger.info("######## " + AutopsyTestCases.getEscapedPath(System.getProperty("img_path")) + " #######");
Timeouts.setDefault("ComponentOperator.WaitComponentTimeout", 1000000); Timeouts.setDefault("ComponentOperator.WaitComponentTimeout", 1000000);
try {
if (Boolean.parseBoolean(System.getProperty("isMultiUser"))) {
// Set up a custom postgres CR using the configuration passed
// to system properties.
CentralRepoDbManager manager = new CentralRepoDbManager();
manager.getDbSettingsPostgres().setHost(System.getProperty("crHost"));
manager.getDbSettingsPostgres().setPort(Integer.parseInt(System.getProperty("crPort")));
manager.getDbSettingsPostgres().setUserName(System.getProperty("crUserName"));
manager.getDbSettingsPostgres().setPassword(System.getProperty("crPassword"));
manager.setupPostgresDb(CentralRepoDbChoice.POSTGRESQL_CUSTOM);
}
} catch (CentralRepoException ex) {
throw new RuntimeException("Error setting up multi user CR", ex);
}
} }
/** /**

View File

@ -471,6 +471,10 @@ class TestRunner(object):
test_data.ant.append("-DsolrPort=" + str(test_config.solrPort)) test_data.ant.append("-DsolrPort=" + str(test_config.solrPort))
test_data.ant.append("-DmessageServiceHost=" + test_config.messageServiceHost) test_data.ant.append("-DmessageServiceHost=" + test_config.messageServiceHost)
test_data.ant.append("-DmessageServicePort=" + str(test_config.messageServicePort)) test_data.ant.append("-DmessageServicePort=" + str(test_config.messageServicePort))
test_data.ant.append("-DcrHost=" + str(test_config.crHost))
test_data.ant.append("-DcrPort=" + str(test_config.crPort))
test_data.ant.append("-DcrUserName=" + str(test_config.crUserName))
test_data.ant.append("-DcrPassword=" + str(test_config.crPassword))
if test_data.isMultiUser: if test_data.isMultiUser:
test_data.ant.append("-DisMultiUser=true") test_data.ant.append("-DisMultiUser=true")
# Note: test_data has autopys_version attribute, but we couldn't see it from here. It's set after run ingest. # Note: test_data has autopys_version attribute, but we couldn't see it from here. It's set after run ingest.
@ -854,6 +858,14 @@ class TestConfiguration(object):
self.messageServicePort = parsed_config.getElementsByTagName("messageServicePort")[0].getAttribute("value").encode().decode("utf_8") self.messageServicePort = parsed_config.getElementsByTagName("messageServicePort")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("multiUser_outdir"): if parsed_config.getElementsByTagName("multiUser_outdir"):
self.multiUser_outdir = parsed_config.getElementsByTagName("multiUser_outdir")[0].getAttribute("value").encode().decode("utf_8") self.multiUser_outdir = parsed_config.getElementsByTagName("multiUser_outdir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("crHost"):
self.crHost = parsed_config.getElementsByTagName("crHost")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("crPort"):
self.crPort = parsed_config.getElementsByTagName("crPort")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("crUserName"):
self.crUserName = parsed_config.getElementsByTagName("crUserName")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("crPassword"):
self.crPassword = parsed_config.getElementsByTagName("crPassword")[0].getAttribute("value").encode().decode("utf_8")
self._init_imgs(parsed_config) self._init_imgs(parsed_config)
self._init_build_info(parsed_config) self._init_build_info(parsed_config)

View File

@ -27,7 +27,7 @@ def make_os_path(platform, *dirs):
path += str(dir).replace('\\', '/') + '/' path += str(dir).replace('\\', '/') + '/'
return path_fix(path) return path_fix(path)
elif platform == "win32": elif platform == "win32":
return make_path(dirs) return make_path(*dirs)
else: else:
print("Couldn't make path, because we only support Windows and Cygwin at this time.") print("Couldn't make path, because we only support Windows and Cygwin at this time.")
sys.exit(1) sys.exit(1)

Binary file not shown.