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

@ -383,6 +383,31 @@ public class CentralRepoDbManager {
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
* were successfully applied.

View File

@ -121,10 +121,12 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
* @return
*/
String getConnectionURL(boolean usePostgresDb) {
StringBuilder url = new StringBuilder();
url.append(getJDBCBaseURI());
url.append(getHost());
url.append("/"); // NON-NLS
StringBuilder url = new StringBuilder()
.append(getJDBCBaseURI())
.append(getHost())
.append(":") // NON-NLS
.append(getPort())
.append("/"); // NON-NLS
if (usePostgresDb) {
url.append("postgres"); // NON-NLS
} else {
@ -153,7 +155,7 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv
} catch (ClassNotFoundException | SQLException ex) {
// TODO: Determine why a connection failure (ConnectionException) re-throws
// 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;
}
return conn;

View File

@ -1,4 +1,10 @@
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.prevCaseComment.text=Previous Case:
# {0} - typeName
@ -7,6 +13,3 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
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 org.openide.modules.ModuleInstall;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.Version;
/**
* 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
* 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
public void restored() {
addApplicationEventListeners();
if (Version.getBuildType() == Version.Type.RELEASE) {
setupDefaultCentralRepository();
}
setupDefaultCentralRepository();
}
/**
@ -107,9 +97,9 @@ public class Installer extends ModuleInstall {
/**
* 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
* unconditionally (not running with a GUI, e.g., in an automated ingest
* node).
* does the set up unconditionally. If the application is running with a
* GUI, a notification will be displayed to the user if the mode is RELEASE
* (in other words, developers are exempt from seeing the notification).
*/
private void setupDefaultCentralRepository() {
Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository");
@ -128,61 +118,29 @@ public class Installer extends ModuleInstall {
}
}
// if central repository hasn't been previously initialized, initialize it
if (!initialized) {
// 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(initialized) {
return; // Nothing to do
}
}
/**
* Sets up a default single-user SQLite central repository.
*
* @throws CentralRepoException If there is an error setting up teh central
* repository.
*/
private void setupDefaultSqliteCentralRepo() throws CentralRepoException {
CentralRepoDbManager manager = new CentralRepoDbManager();
manager.setupDefaultSqliteDb();
if (CentralRepositoryNotificationDialog.shouldDisplay()) {
CentralRepositoryNotificationDialog.display();
}
try {
CentralRepoDbManager manager = new CentralRepoDbManager();
if (UserPreferences.getIsMultiUserModeEnabled()) {
// Set up using existing multi-user settings.
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.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -93,16 +94,56 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
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
* TSK_RECENT_OBJECT artifact.
*
* @param dataSource The data source to query.
* @param maxCount The maximum number of results to return, pass 0 to get
* a list of all results.
* @param maxCount The maximum number of results to return, pass 0 to get a
* list of all results.
*
* @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 TskCoreException
@ -112,36 +153,45 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
List<BlackboardArtifact> artifactList
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT),
dataSource,
DATETIME_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
throwOnNonPositiveCount(maxCount);
List<RecentFileDetails> fileDetails = new ArrayList<>();
for (BlackboardArtifact artifact : artifactList) {
Long accessedTime = null;
String path = "";
List<RecentFileDetails> details = provider.get().getBlackboard()
.getArtifacts(ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), dataSource.getId()).stream()
.map(art -> getRecentlyOpenedDocument(art))
.filter(d -> d != null)
.collect(Collectors.toList());
// Get all the attributes in one call.
List<BlackboardAttribute> attributeList = artifact.getAttributes();
for (BlackboardAttribute attribute : attributeList) {
return getSortedLimited(details, maxCount);
}
if (attribute.getAttributeType().equals(DATETIME_ATT)) {
accessedTime = attribute.getValueLong();
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
path = attribute.getValueString();
}
}
/**
* Returns a RecentDownloadDetails object as derived from the recent
* download 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 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) {
fileDetails.add(new RecentFileDetails(path, accessedTime));
}
if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
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.
*
* @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
*
* @return A list of RecentFileDetails objects or empty list if none were
* found.
* found.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
@ -163,46 +213,23 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
List<BlackboardArtifact> artifactList
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD),
dataSource,
DATETIME_ACCESSED_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
maxCount);
throwOnNonPositiveCount(maxCount);
List<RecentDownloadDetails> fileDetails = new ArrayList<>();
for (BlackboardArtifact artifact : artifactList) {
// Get all the attributes in one call.
Long accessedTime = null;
String domain = "";
String path = "";
List<RecentDownloadDetails> details = provider.get().getBlackboard()
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), dataSource.getId()).stream()
.map(art -> getRecentDownload(art))
.filter(d -> d != null)
.collect(Collectors.toList());
List<BlackboardAttribute> attributeList = artifact.getAttributes();
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;
return getSortedLimited(details, maxCount);
}
/**
* Returns a list of the most recent message attachments.
*
* @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
*
* @return A list of RecentFileDetails of the most recent attachments.
*
@ -214,115 +241,72 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
if (maxCount < 0) {
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
throwOnNonPositiveCount(maxCount);
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.
*
* @return Returns a SortedMap of details objects returned in descending
* order.
*
* @throws SleuthkitCaseProviderException
* @param artifact The associated object artifact.
* @param skCase The current case.
* @return The derived object or null.
* @throws TskCoreException
*/
private SortedMap<Long, List<RecentAttachmentDetails>> buildAttachmentMap(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
SleuthkitCase skCase = provider.get();
TreeMap<Long, List<RecentAttachmentDetails>> sortedMap = new TreeMap<>();
List<BlackboardArtifact> associatedArtifacts = skCase.getBlackboard().getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId());
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;
}
}
private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
// get associated artifact or return no result
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
if (attribute == null) {
return null;
}
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.
*
* @param nodeArtifact An artifact that might be a message. Must not be
* null.
* null.
*
* @return True if the given artifact is a message artifact
*/
@ -330,6 +314,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
final int artifactTypeID = nodeArtifact.getArtifactTypeID();
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.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.
*
* @param path File path.
* @param date File access date\time in seconds with java epoch.
* @param path File path.
* @param date File access date\time in seconds with java epoch.
* @param webDomain The webdomain from which the file was downloaded.
*/
RecentDownloadDetails(String path, long date, String webDomain) {
@ -404,7 +389,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Returns the web domain.
*
* @return The web domain or empty string if not available or
* applicable.
* applicable.
*/
public String getWebDomain() {
return webDomain;
@ -422,10 +407,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* Constructor for recent download files which have a path, date and
* domain value.
*
* @param path File path.
* @param date File crtime.
* @param path File path.
* @param date File crtime.
* @param sender The sender of the message from which the file was
* attached.
* attached.
*/
RecentAttachmentDetails(String path, long date, String sender) {
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 or empty string if not
* available.
* available.
*/
public String getSender() {
return sender;

View File

@ -172,9 +172,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
* @param provider The object providing the current SleuthkitCase.
* @param translationService The translation service.
* @param logger The logger to use.
* @param logger The logger to use.
*/
public UserActivitySummary(
SleuthkitCaseProvider provider,
@ -206,7 +206,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Gets a list of recent domains based on the datasource.
*
* @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.
*
@ -242,12 +242,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Creates a TopDomainsResult from data or null if no visit date exists
* within DOMAIN_WINDOW_MS of mostRecentMs.
*
* @param domain The domain.
* @param visits The number of visits.
* @param domain The domain.
* @param visits The number of visits.
* @param mostRecentMs The most recent visit of any domain.
*
* @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) {
long visitCount = 0;
@ -280,9 +280,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dataSource The datasource.
*
* @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized
* (lowercase; trimmed) domain names to when those domains were
* visited.
* date in milliseconds and the second value maps normalized (lowercase;
* trimmed) domain names to when those domains were visited.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
@ -349,7 +348,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
*
* @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) {
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
@ -364,11 +363,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* term.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
* @param count The maximum number of records to be shown (must be > 0).
*
* @return The list of most recent web searches where most recent search
* appears first.
* appears first.
*
* @throws
* 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());
// group by search string (case insensitive)
Collection<List<TopWebSearchResult>> resultGroups = webSearchArtifacts
Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
.stream()
// get items where search string and date is not null
.map(UserActivitySummary::getWebSearchResult)
// remove null records
.filter(result -> result != null)
// get these messages grouped by search to string
.collect(Collectors.groupingBy((result) -> result.getSearchString().toUpperCase()))
// get the latest message for each search string
.collect(Collectors.toMap(
(result) -> result.getSearchString().toUpperCase(),
result -> result,
(result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values();
// get the most recent date for each search term
List<TopWebSearchResult> results = resultGroups
.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
.sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
.limit(count)
@ -424,7 +423,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param original The original text.
*
* @return The translated text or null if no translation can be determined
* or exists.
* or exists.
*/
private String getTranslationOrNull(String original) {
if (!translationService.hasProvider() || StringUtils.isBlank(original)) {
@ -448,15 +447,34 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
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.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
* @param count The maximum number of records to be shown (must be > 0).
*
* @return The list of most recent devices attached where most recent device
* attached appears first.
* attached appears first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -469,7 +487,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
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)
.stream()
.map(artifact -> {
@ -482,9 +500,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
})
// remove Root Hub identifier
.filter(result -> {
return result.getDeviceModel() == null
return result.getDeviceId() == null
|| result.getDeviceModel() == null
|| !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)
.collect(Collectors.toList());
}
@ -495,7 +518,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
*
* @return The TopAccountResult or null if the account type or message date
* cannot be determined.
* cannot be determined.
*/
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
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
* 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 dateAttrs The date attribute types.
* @param dateAttrs The date attribute types.
*
* @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) {
String type = messageType;
@ -538,11 +561,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* sent.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
* @param count The maximum number of records to be shown (must be > 0).
*
* @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
* 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));
// get them grouped by account type
Collection<List<TopAccountResult>> groupedResults = allResults
Collection<TopAccountResult> groupedResults = allResults
// remove null records
.filter(result -> result != null)
// get these messages grouped by account type
.collect(Collectors.groupingBy(TopAccountResult::getAccountType))
// get these messages grouped by account type and get the most recent of each type
.collect(Collectors.toMap(
result -> result.getAccountType(),
result -> result,
(result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
.values();
// get account type sorted by most recent date
return groupedResults
.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
.sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
// limit to count
@ -608,7 +631,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* 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.
*
* @return The short folder name or empty string if not found.
@ -696,7 +719,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param long2 Second possibly null long.
*
* @return Returns the compare value: 1,0,-1 favoring the higher non-null
* value.
* value.
*/
private static int nullableCompare(Long long1, Long long2) {
if (long1 == null && long2 == null) {
@ -721,7 +744,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return longNum != null && longNum > 0;
}
/**
* Retrieves the top programs results for the given data source limited to
* the count provided as a parameter. The highest run times are at the top
@ -731,12 +753,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* be ignored and all items will be returned.
*
* @param dataSource The datasource. If the datasource is null, an empty
* list will be returned.
* @param count The number of results to return. This value must be > 0
* or an IllegalArgumentException will be thrown.
* list will be returned.
* @param count The number of results to return. This value must be > 0 or
* an IllegalArgumentException will be thrown.
*
* @return The sorted list and limited to the count if last run or run count
* information is available on any item.
* information is available on any item.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
@ -759,7 +781,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
// The value will be a TopProgramsResult with the max run times
// and most recent last run date for each program name / program path pair.
.collect(Collectors.toMap(
res -> Pair.of(res.getProgramName(), res.getProgramPath()),
res -> Pair.of(
res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
res -> res,
(res1, res2) -> {
return new TopProgramsResult(
@ -852,10 +876,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* Main constructor.
*
* @param deviceId The device id.
* @param deviceId The device id.
* @param dateAccessed The date last attached.
* @param deviceMake The device make.
* @param deviceModel The device model.
* @param deviceMake The device make.
* @param deviceModel The device model.
*/
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) {
this.deviceId = deviceId;
@ -906,7 +930,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @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) {
this.accountType = accountType;
@ -940,9 +964,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* Describes a top domain result.
*
* @param domain The domain.
* @param domain The domain.
* @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) {
this.domain = domain;
@ -987,7 +1011,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @param programName The name 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) {
this.programName = programName;

View File

@ -1,3 +1,4 @@
ILeappAnalyzerIngestModule.init.exception.msg=Unable to find {0}.
ILeappAnalyzerIngestModule.processing.file=Processing 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.processing.file=Processing file {0}
ILeappAnalyzerIngestModule.parsing.file=Parsing file {0}
ILeappAnalyzerIngestModule.processing.filesystem=Processing filesystem
ILeappAnalyzerIngestModule.report.name=iLeapp Html Report
ILeappAnalyzerIngestModule.requires.windows=iLeapp module requires windows.
ILeappAnalyzerIngestModule.running.iLeapp=Running iLeapp

View File

@ -18,8 +18,10 @@
*/
package org.sleuthkit.autopsy.modules.ileappanalyzer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
@ -32,14 +34,17 @@ import java.util.Locale;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
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.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
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.Content;
import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.ReadContentInputStream;
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 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_PATHS_FILE = "iLeapp_paths.txt"; //NON-NLS
private File iLeappExecutable;
@ -87,7 +95,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
try {
iLeappFileProcessor = new ILeappFileProcessor();
} catch (IOException | IngestModuleException ex) {
} catch (IOException | IngestModuleException | NoCurrentCaseException ex) {
throw new IngestModuleException(Bundle.ILeappAnalyzerIngestModule_error_ileapp_file_processor_init(), ex);
}
@ -112,58 +120,49 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
@Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
if (!(context.getDataSource() instanceof LocalFilesDataSource)) {
return ProcessResult.OK;
Case currentCase = Case.getCurrentCase();
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);
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;
Case currentCase = Case.getCurrentCase();
for (AbstractFile iLeappFile : iLeappFilesToProcess) {
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;
Integer filesProcessedCount = 0;
for (AbstractFile iLeappFile : iLeappFilesToProcess) {
processILeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, iLeappFile);
filesProcessedCount++;
}
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) {
// 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++;
// Process the logical image as a fs in iLeapp to make sure this is not a logical fs that was added
extractFilesFromImage(dataSource, iLeappPathsToProcess, tempOutputPath);
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
}
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
@ -173,6 +172,99 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
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
*
@ -198,9 +290,9 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
for (AbstractFile iLeappFile : iLeappFiles) {
if (((iLeappFile.getLocalAbsPath() != null)
&& (!iLeappFile.getNameExtension().isEmpty() && (!iLeappFile.isVirtual())))
&& ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")))
|| iLeappFile.getName().toLowerCase().contains(".tgz"))) {
iLeappFilesToProcess.add(iLeappFile);
&& ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")))
|| iLeappFile.getName().toLowerCase().contains(".tgz"))) {
iLeappFilesToProcess.add(iLeappFile);
}
}
@ -208,6 +300,13 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
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) {
ProcessBuilder processBuilder = buildProcessWithRunAsInvoker(
@ -221,10 +320,26 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
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) {
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.
*/
processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
@ -254,7 +369,12 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
.filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
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) {
@ -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.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
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, List<List<String>>> tsvFileAttributes;
public ILeappFileProcessor() throws IOException, IngestModuleException {
Blackboard blkBoard;
public ILeappFileProcessor() throws IOException, IngestModuleException, NoCurrentCaseException {
this.tsvFiles = new HashMap<>();
this.tsvFileArtifacts = new HashMap<>();
this.tsvFileArtifactComments = new HashMap<>();
this.tsvFileAttributes = new HashMap<>();
blkBoard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
configExtractor();
loadConfigFile();
@ -110,6 +115,19 @@ public final class ILeappFileProcessor {
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
* 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());
for (String tsvFile : allTsvFiles) {
if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile))) {
if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile.toLowerCase()))) {
foundTsvFiles.add(tsvFile);
}
}
@ -160,7 +178,41 @@ public final class ILeappFileProcessor {
processFile(iLeappFile, attrList, fileName, artifactType, bbartifacts, iLeappImageFile);
} 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);
}
}
@ -174,7 +226,8 @@ public final class ILeappFileProcessor {
}
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))) {
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.
@ -183,8 +236,8 @@ public final class ILeappFileProcessor {
line = reader.readLine();
while (line != null) {
Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName);
if (!bbattributes.isEmpty()) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), iLeappImageFile, bbattributes);
if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
if (bbartifact != null) {
bbartifacts.add(bbartifact);
}
@ -235,7 +288,7 @@ public final class ILeappFileProcessor {
}
private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, Integer columnNumber, BlackboardAttribute.Type attributeType,
String fileName) {
String fileName) {
if (attrType.matches("STRING")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber]));
} else if (attrType.matches("INTEGER")) {
@ -340,7 +393,7 @@ public final class ILeappFileProcessor {
for (int i = 0; i < nlist.getLength(); i++) {
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
*
* @param type is a blackboard.artifact_type enum to determine
* which type the artifact should be
* @param abstractFile is the AbstractFile 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
*/
/**
* 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 abstractFile is the AbstractFile 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, AbstractFile abstractFile, Collection<BlackboardAttribute> bbattributes) {
try {
BlackboardArtifact bbart = abstractFile.newArtifact(type);
@ -417,6 +471,30 @@ public final class ILeappFileProcessor {
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.
*

View File

@ -47,6 +47,15 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_BLUETOOTH_ADAPTER" comment="Bluetooth Other">
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
@ -120,6 +129,13 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Activity">
<AttributeName attributename="TSK_DATETIME" columnName="Entry Creation" required="yes" />
@ -189,6 +205,36 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_BLUETOOTH_PAIRING" comment="KnowledgeC Bluetooth Connections">
<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">
<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="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 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="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" />
</ArtifactName>
</FileName>
@ -248,6 +340,19 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_RECENT_OBJ" comment="KnowledgeC Media Playing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -288,6 +393,36 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Safari Browsing">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -302,6 +437,18 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_PROG_RUN" comment="KnowledgeC App Usage">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
@ -318,6 +465,18 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="KnowledgeC Web Usage">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Start" required="yes" />
@ -433,6 +592,102 @@
</ArtifactName>
</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">
<ArtifactName artifactname="TSK_WIFI_NETWORK" comment="Powerlog WiFi Network Connections">
<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.
*
* @param recentFilesSummary The RecentFilesSummary object.
* @param dataSource The datasource.
* @param count The number of items to retrieve.
* @param dataSource The datasource.
* @param count The number of items to retrieve.
*
* @return The method's return data.
*
@ -95,7 +95,7 @@ public class RecentFilesSummaryTest {
/**
* 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
*
* @throws TskCoreException
@ -137,7 +137,7 @@ public class RecentFilesSummaryTest {
* SleuthkitCase isn't called.
*
* @param recentFilesMethod The method to call.
* @param methodName The name of the method
* @param methodName The name of the method
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
@ -175,7 +175,7 @@ public class RecentFilesSummaryTest {
* If SleuthkitCase returns no results, an empty list is returned.
*
* @param recentFilesMethod The method to call.
* @param methodName The name of the method.
* @param methodName The name of the method.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
@ -220,11 +220,11 @@ public class RecentFilesSummaryTest {
/**
* Gets a mock BlackboardArtifact.
*
* @param ds The data source to which the artifact belongs.
* @param artifactId The artifact id.
* @param artType The artifact type.
* @param ds The data source to which the artifact belongs.
* @param artifactId The artifact id.
* @param artType The artifact type.
* @param attributeArgs The mapping of attribute type to value for each
* attribute in the artifact.
* attribute in the artifact.
*
* @return The mock artifact.
*/
@ -247,10 +247,10 @@ public class RecentFilesSummaryTest {
/**
* 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 dateTime The time in seconds from epoch.
* @param path The path for the document.
* @param dateTime The time in seconds from epoch.
* @param path The path for the document.
*
* @return The mock artifact with pertinent attributes.
*/
@ -292,6 +292,26 @@ 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
public void getRecentlyOpenedDocuments_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1);
@ -315,11 +335,11 @@ public class RecentFilesSummaryTest {
/**
* Creates a mock blackboard artifact for getRecentDownloads.
*
* @param ds The datasource.
* @param ds The datasource.
* @param artifactId The artifact id.
* @param dateTime The time in seconds from epoch.
* @param domain The domain.
* @param path The path for the download.
* @param dateTime The time in seconds from epoch.
* @param domain The domain.
* @param path The path for the download.
*
* @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
public void getRecentDownloads_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
DataSource dataSource = TskMockUtils.getDataSource(1);
@ -409,19 +453,17 @@ public class RecentFilesSummaryTest {
* Constructor with all parameters.
*
* @param messageArtifactTypeId The type id for the artifact or null if
* no message artifact to be created.
* @param emailFrom Who the message is from or null not to
* include attribute.
* @param messageTime Time in seconds from epoch or null not
* to include attribute.
* @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename
* value.
* @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT
* artifact has no attribute (even though
* it is required).
* @param hasParent Whether or not the artifact has a parent
* AbstractFile.
* no message artifact to be created.
* @param emailFrom Who the message is from or null not to include
* attribute.
* @param messageTime Time in seconds from epoch or null not to include
* attribute.
* @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename value.
* @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT
* artifact has no attribute (even though it is required).
* @param hasParent Whether or not the artifact has a parent
* AbstractFile.
*/
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime,
String fileParentPath, String fileName,
@ -441,14 +483,13 @@ public class RecentFilesSummaryTest {
* SleuthkitCase assumed.
*
* @param messageArtifactTypeId The type id for the artifact or null if
* no message artifact to be created.
* @param emailFrom Who the message is from or null not to
* include attribute.
* @param messageTime Time in seconds from epoch or null not
* to include attribute.
* @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename
* value.
* no message artifact to be created.
* @param emailFrom Who the message is from or null not to include
* attribute.
* @param messageTime Time in seconds from epoch or null not to include
* attribute.
* @param fileParentPath The parent AbstractFile's path value.
* @param fileName The parent AbstractFile's filename value.
*/
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime, String fileParentPath, String fileName) {
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.
*
* @param artifacts The mapping of artifact id to artifact.
* @param item The record to setup.
* @param dataSource The datasource.
* @param artifacts The mapping of artifact id to artifact.
* @param item The record to setup.
* @param dataSource The datasource.
* @param associatedId The associated attribute id.
* @param artifactId The artifact id.
* @param artifactId The artifact id.
*
* @return The associated Artifact blackboard attribute.
*
@ -534,7 +575,7 @@ public class RecentFilesSummaryTest {
* to return pertinent data.
*
* @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.
*/
@ -678,4 +719,34 @@ public class RecentFilesSummaryTest {
.toString().equalsIgnoreCase(successItem2Details.getPath()));
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,12 +66,16 @@ import org.sleuthkit.datamodel.TskCoreException;
* Tests for UserActivitySummary.
*/
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> {
/**
* A UserActivitySummary method encapsulated in a uniform manner.
*
* @param userActivitySummary The UserActivitySummary class to use.
* @param datasource The data source.
* @param count The count.
@ -117,8 +121,8 @@ public class UserActivitySummaryTest {
/**
* Gets a UserActivitySummary class to test.
*
* @param tskCase The SleuthkitCase.
* @param hasTranslation Whether the translation service is functional.
* @param tskCase The SleuthkitCase.
* @param hasTranslation Whether the translation service is functional.
* @param translateFunction Function for translation.
*
* @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) {
try {
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 dataSource The datasource.
* @param dateRcvd The date received in seconds or null to exclude.
* @param dateSent The date sent 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.
*
* @return The mock artifact.
*/
@ -738,8 +764,8 @@ public class UserActivitySummaryTest {
*
* @param artifactId The artifact id.
* @param dataSource The datasource.
* @param dateStart The date start in seconds or null to exclude.
* @param dateEnd The date end 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.
*
* @return The mock artifact.
*/
@ -768,8 +794,8 @@ public class UserActivitySummaryTest {
*
* @param artifactId The artifact id.
* @param dataSource The datasource.
* @param type The account type.
* @param dateSent The date of the message in seconds.
* @param type The account type.
* @param dateSent The date of the message in seconds.
*/
private static BlackboardArtifact getMessageArtifact(long artifactId, DataSource dataSource, String type, Long dateTime) {
List<BlackboardAttribute> attributes = new ArrayList<>();
@ -794,11 +820,11 @@ public class UserActivitySummaryTest {
/**
* Performs a test on UserActivitySummary.getRecentAccounts.
*
* @param dataSource The datasource to use as parameter.
* @param count The count to use as a parameter.
* @param retArtifacts The artifacts to return from
* SleuthkitCase.getArtifacts. This method filters
* based on artifact type from the call.
* @param dataSource The datasource to use as parameter.
* @param count The count to use as a parameter.
* @param retArtifacts The artifacts to return from
* SleuthkitCase.getArtifacts. This method filters based on artifact type
* from the call.
* @param expectedResults The expected results.
*
* @throws TskCoreException

View File

@ -76,6 +76,10 @@
<sysproperty key="solrPort" value="${solrPort}"/>
<sysproperty key="messageServiceHost" value="${messageServiceHost}"/>
<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}"/>
<!--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}"/>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -25,6 +25,9 @@ import junit.framework.Test;
import junit.framework.TestCase;
import org.netbeans.jemmy.Timeouts;
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
@ -100,6 +103,21 @@ public class RegressionTest extends TestCase {
public void setUp() {
logger.info("######## " + AutopsyTestCases.getEscapedPath(System.getProperty("img_path")) + " #######");
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("-DmessageServiceHost=" + test_config.messageServiceHost)
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:
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.
@ -854,6 +858,14 @@ class TestConfiguration(object):
self.messageServicePort = parsed_config.getElementsByTagName("messageServicePort")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("multiUser_outdir"):
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_build_info(parsed_config)

View File

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

Binary file not shown.