mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
commit
80bc0a5434
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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?
|
||||
|
@ -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>"
|
||||
);
|
||||
}
|
||||
}
|
@ -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,20 +80,11 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the application event listeners responsible for adding data to the
|
||||
@ -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,62 +118,30 @@ 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(initialized) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
|
||||
dialogText,
|
||||
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"),
|
||||
JOptionPane.YES_NO_OPTION)) {
|
||||
if (CentralRepositoryNotificationDialog.shouldDisplay()) {
|
||||
CentralRepositoryNotificationDialog.display();
|
||||
}
|
||||
|
||||
setupDefaultSqliteCentralRepo();
|
||||
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);
|
||||
}
|
||||
});
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a central repository exception in a message box if running with a
|
||||
|
@ -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,13 +94,53 @@ 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.
|
||||
@ -112,38 +153,47 @@ 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 (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return new RecentDownloadDetails(path, accessedTime, domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (accessedTime != null && accessedTime != 0) {
|
||||
fileDetails.add(new RecentFileDetails(path, accessedTime));
|
||||
/**
|
||||
* 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.");
|
||||
}
|
||||
}
|
||||
|
||||
return fileDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the most recent downloads based on the value of the the
|
||||
* artifact TSK_DATETIME_ACCESSED attribute.
|
||||
@ -163,38 +213,15 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,109 +241,66 @@ 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) {
|
||||
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) {
|
||||
continue;
|
||||
return null;
|
||||
}
|
||||
|
||||
// get associated message artifact if exists or return no result
|
||||
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 = "";
|
||||
if (messageArtifact == null || !isMessageArtifact(messageArtifact)) {
|
||||
return null;
|
||||
}
|
||||
senderAttribute = messageArtifact.getAttribute(MSG_DATEIME_SENT_ATT);
|
||||
if (senderAttribute != null) {
|
||||
date = senderAttribute.getValueLong();
|
||||
|
||||
// get abstract file if exists or return no result
|
||||
Content content = artifact.getParent();
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AbstractFile abstractFile = (AbstractFile) content;
|
||||
|
||||
path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString();
|
||||
// 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) {
|
||||
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);
|
||||
if (date == null || date == 0 || StringUtils.isBlank(path)) {
|
||||
return null;
|
||||
} else {
|
||||
break;
|
||||
return new RecentAttachmentDetails(path, date, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given artifact a message.
|
||||
@ -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();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
@ -364,8 +363,7 @@ 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.
|
||||
@ -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)
|
||||
@ -448,12 +447,31 @@ 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.
|
||||
@ -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());
|
||||
}
|
||||
@ -538,8 +561,7 @@ 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.
|
||||
@ -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
|
||||
@ -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
|
||||
@ -732,8 +754,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
*
|
||||
* @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.
|
||||
* @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.
|
||||
@ -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(
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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,59 +120,50 @@ 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<>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
processILeappFile(dataSource, currentCase, statusHelper, filesProcessedCount, iLeappFile);
|
||||
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,
|
||||
Bundle.ILeappAnalyzerIngestModule_has_run(),
|
||||
@ -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
|
||||
*
|
||||
@ -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,8 +369,13 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
|
||||
.filter(f -> f.toLowerCase().endsWith("index.html")).collect(Collectors.toList());
|
||||
|
||||
if (!allIndexFiles.isEmpty()) {
|
||||
// Check for existance of directory that holds report data if does not exist then report contains no data
|
||||
String filePath = FilenameUtils.getFullPathNoEndSeparator(allIndexFiles.get(0));
|
||||
File dataFilesDir = new File(Paths.get(filePath, "_TSV Exports").toString());
|
||||
if (dataFilesDir.exists()) {
|
||||
currentCase.addReport(allIndexFiles.get(0), MODULE_NAME, Bundle.ILeappAnalyzerIngestModule_report_name());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException | UncheckedIOException | TskCoreException ex) {
|
||||
// catch the error and continue on as report is not added
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
@ -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,16 +446,17 @@ 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 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
|
||||
* @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
|
||||
*/
|
||||
@ -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.
|
||||
*
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
@ -410,16 +454,14 @@ public class RecentFilesSummaryTest {
|
||||
*
|
||||
* @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 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 fileName The parent AbstractFile's filename value.
|
||||
* @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT
|
||||
* artifact has no attribute (even though
|
||||
* it is required).
|
||||
* artifact has no attribute (even though it is required).
|
||||
* @param hasParent Whether or not the artifact has a parent
|
||||
* AbstractFile.
|
||||
*/
|
||||
@ -442,13 +484,12 @@ public class RecentFilesSummaryTest {
|
||||
*
|
||||
* @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 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 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);
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
@ -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,
|
||||
@ -797,8 +823,8 @@ public class UserActivitySummaryTest {
|
||||
* @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.
|
||||
* SleuthkitCase.getArtifacts. This method filters based on artifact type
|
||||
* from the call.
|
||||
* @param expectedResults The expected results.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
|
@ -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}"/>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
BIN
thirdparty/iLeapp/ileapp.exe
vendored
BIN
thirdparty/iLeapp/ileapp.exe
vendored
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user