Merge branch 'develop' of github.com:sleuthkit/autopsy into java11-upgrade

This commit is contained in:
esaunders 2020-09-11 12:04:36 -04:00
commit ca204cbd60
30 changed files with 1231 additions and 300 deletions

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.casemodule; package org.sleuthkit.autopsy.casemodule;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestStream; import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.ingest.IngestStreamClosedException; import org.sleuthkit.autopsy.ingest.IngestStreamClosedException;
@ -35,6 +36,11 @@ class DefaultIngestStream implements IngestStream {
public void addFiles(List<Long> fileObjectIds) throws IngestStreamClosedException { public void addFiles(List<Long> fileObjectIds) throws IngestStreamClosedException {
// Do nothing // Do nothing
} }
@Override
public IngestJob getIngestJob() {
throw new UnsupportedOperationException("DefaultIngestStream has no associated IngestJob");
}
@Override @Override
public synchronized boolean isClosed() { public synchronized boolean isClosed() {

View File

@ -464,6 +464,42 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack); doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
} }
@Override
public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
this.deviceId = deviceId;
this.imagePath = dataSourcePath.toString();
this.sectorSize = 0;
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false;
setDataSourceOptionsCalled = true;
// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return null;
}
// Now initialize the ingest stream
try {
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return null;
}
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callBack);
return ingestStream;
}
/** /**
* Sets the configuration of the data source processor without using the * Sets the configuration of the data source processor without using the

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -97,68 +97,64 @@ import org.sleuthkit.datamodel.TskCoreException;
* Image viewer part of the Media View layered pane. Uses JavaFX to display the * Image viewer part of the Media View layered pane. Uses JavaFX to display the
* image. * image.
*/ */
@NbBundle.Messages({"MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E", @NbBundle.Messages({
"MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E",
"MediaViewImagePanel.errorLabel.text=Could not load file into Media View.", "MediaViewImagePanel.errorLabel.text=Could not load file into Media View.",
"MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory."}) "MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory."
})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel { class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel {
private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm()); private static final long serialVersionUID = 1L;
private final static Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName()); private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName());
private static final double[] ZOOM_STEPS = {
private final boolean fxInited; 0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
private JFXPanel fxPanel; private static final double MIN_ZOOM_RATIO = 0.0625; // 6.25%
private AbstractFile file; private static final double MAX_ZOOM_RATIO = 10.0; // 1000%
private static final Image externalImage = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm());
private static final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
private static final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
.map("."::concat) //NOI18N
.collect(Collectors.toList());
/*
* JFX components
*/
private final ProgressBar progressBar = new ProgressBar();
private final MaskerPane maskerPane = new MaskerPane();
private Group masterGroup; private Group masterGroup;
private ImageTagsGroup tagsGroup; private ImageTagsGroup tagsGroup;
private ImageTagCreator imageTagCreator; private ImageTagCreator imageTagCreator;
private ImageView fxImageView; private ImageView fxImageView;
private ScrollPane scrollPane; private ScrollPane scrollPane;
private final ProgressBar progressBar = new ProgressBar(); private Task<Image> readImageTask;
private final MaskerPane maskerPane = new MaskerPane();
/*
* Swing components
*/
private final JPopupMenu imageTaggingOptions = new JPopupMenu(); private final JPopupMenu imageTaggingOptions = new JPopupMenu();
private final JMenuItem createTagMenuItem; private final JMenuItem createTagMenuItem;
private final JMenuItem deleteTagMenuItem; private final JMenuItem deleteTagMenuItem;
private final JMenuItem hideTagsMenuItem; private final JMenuItem hideTagsMenuItem;
private final JMenuItem exportTagsMenuItem; private final JMenuItem exportTagsMenuItem;
private final JFileChooser exportChooser; private final JFileChooser exportChooser;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private JFXPanel fxPanel;
/*
* State
*/
private final boolean fxInited;
private double zoomRatio; private double zoomRatio;
private double rotation; // Can be 0, 90, 180, and 270. private double rotation; // Can be 0, 90, 180, and 270.
private boolean autoResize = true; // Auto resize when the user changes the size of the content viewer unless the user has used the zoom buttons.
private boolean autoResize = true; // Auto resize when the user changes the size private AbstractFile file;
// of the content viewer unless the user has used the zoom buttons.
private static final double[] ZOOM_STEPS = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
private static final double MIN_ZOOM_RATIO = 0.0625; // 6.25%
private static final double MAX_ZOOM_RATIO = 10.0; // 1000%
static { static {
ImageIO.scanForPlugins(); ImageIO.scanForPlugins();
} }
/**
* mime types we should be able to display. if the mimetype is unknown we
* will fall back on extension and jpg/png header
*/
static private final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
/**
* extensions we should be able to display
*/
static private final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
.map("."::concat) //NOI18N
.collect(Collectors.toList());
private Task<Image> readImageTask;
/** /**
* Creates new form MediaViewImagePanel * Creates new form MediaViewImagePanel
*/ */
@ -168,7 +164,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
"MediaViewImagePanel.hideTagOption=Hide", "MediaViewImagePanel.hideTagOption=Hide",
"MediaViewImagePanel.exportTagOption=Export" "MediaViewImagePanel.exportTagOption=Export"
}) })
public MediaViewImagePanel() { MediaViewImagePanel() {
initComponents(); initComponents();
fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited(); fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited();
@ -354,14 +350,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
} }
private void showErrorNode(String errorMessage, AbstractFile file) { private void showErrorNode(String errorMessage, AbstractFile file) {
final Button externalViewerButton = new Button(Bundle.MediaViewImagePanel_externalViewerButton_text(), new ImageView(EXTERNAL)); final Button externalViewerButton = new Button(Bundle.MediaViewImagePanel_externalViewerButton_text(), new ImageView(externalImage));
externalViewerButton.setOnAction(actionEvent /*
-> //fx ActionEvent * Tie a Swing action (ExternalViewerAction) to a JFX button action.
/* */
* TODO: why is the name passed into the action constructor? it externalViewerButton.setOnAction(actionEvent ->
* means we duplicate this string all over the place -jm new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file))
*/ new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file)) .actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""))
.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent
); );
final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton); final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton);
@ -420,7 +415,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
"state", null, State.NONEMPTY)); "state", null, State.NONEMPTY));
} }
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS logger.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS
} }
scrollPane.setContent(masterGroup); scrollPane.setContent(masterGroup);
} else { } else {
@ -693,14 +688,14 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed
autoResize = false; autoResize = false;
rotation = (rotation + 270) % 360; rotation = (rotation + 270) % 360;
updateView(); updateView();
}//GEN-LAST:event_rotateLeftButtonActionPerformed }//GEN-LAST:event_rotateLeftButtonActionPerformed
private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed
autoResize = false; autoResize = false;
rotation = (rotation + 90) % 360; rotation = (rotation + 90) % 360;
updateView(); updateView();
}//GEN-LAST:event_rotateRightButtonActionPerformed }//GEN-LAST:event_rotateRightButtonActionPerformed
@ -760,7 +755,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(contentViewerTag.getContentTag()); Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(contentViewerTag.getContentTag());
tagsGroup.getChildren().remove(tagInFocus); tagsGroup.getChildren().remove(tagInFocus);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not delete image tag in case db", ex); //NON-NLS logger.log(Level.WARNING, "Could not delete image tag in case db", ex); //NON-NLS
} }
scrollPane.setCursor(Cursor.DEFAULT); scrollPane.setCursor(Cursor.DEFAULT);
@ -793,7 +788,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
ImageTag imageTag = buildImageTag(contentViewerTag); ImageTag imageTag = buildImageTag(contentViewerTag);
tagsGroup.getChildren().add(imageTag); tagsGroup.getChildren().add(imageTag);
} catch (TskCoreException | SerializationException | NoCurrentCaseException ex) { } catch (TskCoreException | SerializationException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS logger.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS
} }
scrollPane.setCursor(Cursor.DEFAULT); scrollPane.setCursor(Cursor.DEFAULT);
@ -832,7 +827,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
ImageTagRegion newRegion = (ImageTagRegion) edit.getNewValue(); ImageTagRegion newRegion = (ImageTagRegion) edit.getNewValue();
ContentViewerTagManager.updateTag(contentViewerTag, newRegion); ContentViewerTagManager.updateTag(contentViewerTag, newRegion);
} catch (SerializationException | TskCoreException | NoCurrentCaseException ex) { } catch (SerializationException | TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS logger.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS
} }
scrollPane.setCursor(Cursor.DEFAULT); scrollPane.setCursor(Cursor.DEFAULT);
}); });
@ -916,7 +911,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX. } catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX.
//This ensures we (devs and users) have something when it doesn't work. //This ensures we (devs and users) have something when it doesn't work.
LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS logger.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport()); JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport());
} }
return null; return null;

View File

@ -26,7 +26,7 @@ CallLogArtifactViewer_label_from=From
CallLogArtifactViewer_label_to=To CallLogArtifactViewer_label_to=To
CallLogArtifactViewer_suffix_local=(Local) CallLogArtifactViewer_suffix_local=(Local)
CallLogArtifactViewer_value_unknown=Unknown CallLogArtifactViewer_value_unknown=Unknown
#{0} - contact name # {0} - contact name
CommunicationArtifactViewerHelper_contact_label=Contact: {0} CommunicationArtifactViewerHelper_contact_label=Contact: {0}
CommunicationArtifactViewerHelper_contact_label_unknown=Unknown CommunicationArtifactViewerHelper_contact_label_unknown=Unknown
CommunicationArtifactViewerHelper_menuitem_copy=Copy CommunicationArtifactViewerHelper_menuitem_copy=Copy

View File

@ -440,7 +440,7 @@ final class CommunicationArtifactViewerHelper {
* @return A JLabel with the contact information. * @return A JLabel with the contact information.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"#{0} - contact name", "# {0} - contact name",
"CommunicationArtifactViewerHelper_contact_label=Contact: {0}", "CommunicationArtifactViewerHelper_contact_label=Contact: {0}",
"CommunicationArtifactViewerHelper_contact_label_unknown=Unknown" "CommunicationArtifactViewerHelper_contact_label_unknown=Unknown"
}) })

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2019 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,28 +31,31 @@ import org.apache.commons.lang3.SystemUtils;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
/** /**
* Executes a command line using an operating system process with a configurable * Executes a command line using an operating system process with pluggable
* timeout and pluggable logic to kill or continue the process on timeout. * logic to terminate the process under certain conditions.
*/ */
public final class ExecUtil { public final class ExecUtil {
private static final Logger logger = Logger.getLogger(ExecUtil.class.getName()); private static final Logger logger = Logger.getLogger(ExecUtil.class.getName());
private static final long DEFAULT_CHECK_INTERVAL = 5; private static final long DEFAULT_TERMINATION_CHECK_INTERVAL = 5;
private static final TimeUnit DEFAULT_CHECK_INTERVAL_UNITS = TimeUnit.SECONDS; private static final TimeUnit DEFAULT_TERMINATION_CHECK_INTERVAL_UNITS = TimeUnit.SECONDS;
private static final long MAX_WAIT_FOR_TERMINATION = 1;
private static final TimeUnit MAX_WAIT_FOR_TERMINATION_UNITS = TimeUnit.MINUTES;
/** /**
* The execute() methods do a wait() with a timeout on the executing process * An interface for defining the conditions under which an operating system
* and query a process terminator each time the timeout expires to determine * process spawned by an ExecUtil method should be terminated.
* whether or not to kill the process. See *
* Some existing implementations: TimedProcessTerminator,
* InterruptedThreadProcessTerminator,
* DataSourceIngestModuleProcessTerminator and * DataSourceIngestModuleProcessTerminator and
* FileIngestModuleProcessTerminator as examples of ProcessTerminator * FileIngestModuleProcessTerminator.
* implementations.
*/ */
public interface ProcessTerminator { public interface ProcessTerminator {
/** /**
* Decides whether or not to terminate a process being run by a * Decides whether or not to terminate a process being run by an
* ExcUtil.execute() methods. * ExecUtil method.
* *
* @return True or false. * @return True or false.
*/ */
@ -78,11 +81,11 @@ public final class ExecUtil {
public static class TimedProcessTerminator implements ProcessTerminator { public static class TimedProcessTerminator implements ProcessTerminator {
private final long startTimeInSeconds; private final long startTimeInSeconds;
private final long maxRunTimeInSeconds; private final Long maxRunTimeInSeconds;
/** /**
* Creates a process terminator that can be used to kill a process after * Creates a process terminator that can be used to kill a process after
* it has run for a given period of time. * it exceeds a maximum allowable run time.
* *
* @param maxRunTimeInSeconds The maximum allowable run time in seconds. * @param maxRunTimeInSeconds The maximum allowable run time in seconds.
*/ */
@ -93,32 +96,41 @@ public final class ExecUtil {
/** /**
* Creates a process terminator that can be used to kill a process after * Creates a process terminator that can be used to kill a process after
* it has run for a given period of time. Maximum allowable run time is * it exceeds a global maximum allowable run time specified as a user
* set via Autopsy Options panel. If the process termination * preference. If the user preference is not set, this terminator has no
* functionality is disabled then the maximum allowable time is set to * effect.
* MAX_INT seconds.
*/ */
public TimedProcessTerminator() { public TimedProcessTerminator() {
if (UserPreferences.getIsTimeOutEnabled() && UserPreferences.getProcessTimeOutHrs() > 0) { if (UserPreferences.getIsTimeOutEnabled() && UserPreferences.getProcessTimeOutHrs() > 0) {
// user specified time out this.maxRunTimeInSeconds = (long) UserPreferences.getProcessTimeOutHrs() * 3600;
this.maxRunTimeInSeconds = UserPreferences.getProcessTimeOutHrs() * 3600;
} else { } else {
// never time out this.maxRunTimeInSeconds = null;
this.maxRunTimeInSeconds = Long.MAX_VALUE;
} }
this.startTimeInSeconds = (new Date().getTime()) / 1000; this.startTimeInSeconds = (new Date().getTime()) / 1000;
} }
@Override @Override
public boolean shouldTerminateProcess() { public boolean shouldTerminateProcess() {
long currentTimeInSeconds = (new Date().getTime()) / 1000; if (maxRunTimeInSeconds != null) {
return (currentTimeInSeconds - this.startTimeInSeconds) > this.maxRunTimeInSeconds; long currentTimeInSeconds = (new Date().getTime()) / 1000;
return (currentTimeInSeconds - this.startTimeInSeconds) > this.maxRunTimeInSeconds;
} else {
return false;
}
} }
} }
/** /**
* Runs a process without a termination check interval or process * Runs a process without a process terminator. This method should be used
* terminator. * with caution because there is nothing to stop the process from running
* forever.
*
* IMPORTANT: This method blocks while the process is running. For legacy
* API reasons, if there is an interrupt the InterruptedException is wrapped
* in an IOException instead of being thrown. Callers that need to know
* about interrupts to detect backgound task cancellation can call
* Thread.isInterrupted() or, if the thread's interrupt flag should be
* cleared, Thread.interrupted().
* *
* @param processBuilder A process builder used to configure and construct * @param processBuilder A process builder used to configure and construct
* the process to be run. * the process to be run.
@ -127,7 +139,8 @@ public final class ExecUtil {
* *
* @throws SecurityException If a security manager exists and vetoes any * @throws SecurityException If a security manager exists and vetoes any
* aspect of running the process. * aspect of running the process.
* @throws IOException If an I/O error occurs. * @throws IOException If an error occurs while executing or
* terminating the process.
*/ */
public static int execute(ProcessBuilder processBuilder) throws SecurityException, IOException { public static int execute(ProcessBuilder processBuilder) throws SecurityException, IOException {
return ExecUtil.execute(processBuilder, 30, TimeUnit.DAYS, new ProcessTerminator() { return ExecUtil.execute(processBuilder, 30, TimeUnit.DAYS, new ProcessTerminator() {
@ -142,6 +155,13 @@ public final class ExecUtil {
* Runs a process using the default termination check interval and a process * Runs a process using the default termination check interval and a process
* terminator. * terminator.
* *
* IMPORTANT: This method blocks while the process is running. For legacy
* API reasons, if there is an interrupt the InterruptedException is wrapped
* in an IOException instead of being thrown. Callers that need to know
* about interrupts to detect backgound task cancellation can call
* Thread.isInterrupted() or, if the thread's interrupt flag should be
* cleared, Thread.interrupted().
*
* @param processBuilder A process builder used to configure and construct * @param processBuilder A process builder used to configure and construct
* the process to be run. * the process to be run.
* @param terminator The terminator. * @param terminator The terminator.
@ -150,16 +170,24 @@ public final class ExecUtil {
* *
* @throws SecurityException If a security manager exists and vetoes any * @throws SecurityException If a security manager exists and vetoes any
* aspect of running the process. * aspect of running the process.
* @throws IOException If an I/O error occurs. * @throws IOException If an error occurs while executing or
* terminating the process.
*/ */
public static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator) throws SecurityException, IOException { public static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator) throws SecurityException, IOException {
return ExecUtil.execute(processBuilder, ExecUtil.DEFAULT_CHECK_INTERVAL, ExecUtil.DEFAULT_CHECK_INTERVAL_UNITS, terminator); return ExecUtil.execute(processBuilder, ExecUtil.DEFAULT_TERMINATION_CHECK_INTERVAL, ExecUtil.DEFAULT_TERMINATION_CHECK_INTERVAL_UNITS, terminator);
} }
/** /**
* Runs a process using a custom termination check interval and a process * Runs a process using a custom termination check interval and a process
* terminator. * terminator.
* *
* IMPORTANT: This method blocks while the process is running. For legacy
* API reasons, if there is an interrupt the InterruptedException is wrapped
* in an IOException instead of being thrown. Callers that need to know
* about interrupts to detect backgound task cancellation can call
* Thread.isInterrupted() or, if the thread's interrupt flag should be
* cleared, Thread.interrupted().
*
* @param processBuilder A process builder used to configure and * @param processBuilder A process builder used to configure and
* construct the process to be run. * construct the process to be run.
* @param terminationCheckInterval The interval at which to query the * @param terminationCheckInterval The interval at which to query the
@ -173,12 +201,52 @@ public final class ExecUtil {
* *
* @throws SecurityException If a security manager exists and vetoes any * @throws SecurityException If a security manager exists and vetoes any
* aspect of running the process. * aspect of running the process.
* @throws IOException If an I/O error occurs. * @throws IOException If an error occurs while executing or
* terminating the process.
*/ */
public static int execute(ProcessBuilder processBuilder, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws SecurityException, IOException { public static int execute(ProcessBuilder processBuilder, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws SecurityException, IOException {
return waitForTermination(processBuilder.command().get(0), processBuilder.start(), terminationCheckInterval, units, terminator); return waitForTermination(processBuilder.command().get(0), processBuilder.start(), terminationCheckInterval, units, terminator);
} }
/**
* Waits for an existing process to finish, using a custom termination check
* interval and a process terminator.
*
* IMPORTANT: This method blocks while the process is running. For legacy
* API reasons, if there is an interrupt the InterruptedException is wrapped
* in an IOException instead of being thrown. Callers that need to know
* about interrupts to detect backgound task cancellation can call
* Thread.isInterrupted() or, if the thread's interrupt flag should be
* cleared, Thread.interrupted().
*
* @param processName The name of the process, for logging
* purposes.
* @param process The process.
* @param terminationCheckInterval The interval at which to query the
* process terminator to see if the process
* should be killed.
* @param units The units for the termination check
* interval.
* @param terminator The process terminator.
*
* @return The exit value of the process.
*
* @throws IOException If an error occurs while executing or terminating the
* process.
*/
public static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws IOException {
try {
return waitForProcess(processName, process, terminationCheckInterval, units, terminator);
} catch (InterruptedException ex) {
/*
* Reset the interrupted flag and wrap the exception in an
* IOException for backwards compatibility.
*/
Thread.currentThread().interrupt();
throw new IOException(String.format("Interrupted executing %s", processName), ex); //NON-NLS
}
}
/** /**
* Waits for an existing process to finish, using a custom termination check * Waits for an existing process to finish, using a custom termination check
* interval and a process terminator. * interval and a process terminator.
@ -195,67 +263,114 @@ public final class ExecUtil {
* *
* @return The exit value of the process. * @return The exit value of the process.
* *
* @throws SecurityException If a security manager exists and vetoes any * @throws IOException If an error occurs while executing or
* aspect of running the process. * terminating the process.
* @throws IOException If an I/O error occurs. * @throws InterruptedException If the thread running this code is
* interrupted while the process is running.
*/ */
public static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws SecurityException, IOException { private static int waitForProcess(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws IOException, InterruptedException {
try { do {
do {
process.waitFor(terminationCheckInterval, units);
if (process.isAlive() && terminator.shouldTerminateProcess()) {
killProcess(process);
try {
process.waitFor();
} catch (InterruptedException ex) {
logger.log(Level.WARNING, String.format("Thread running %s was interrupted before the process completed", processName), ex);
}
}
} while (process.isAlive());
} catch (InterruptedException ex) {
if (process.isAlive()) {
killProcess(process);
}
try { try {
process.waitFor(); //waiting to help ensure process is shutdown before calling interrupt() or returning process.waitFor(terminationCheckInterval, units);
} catch (InterruptedException exx) { } catch (InterruptedException ex) {
logger.log(Level.WARNING, String.format("Thread running %s was interrupted before the process completed", processName), exx); logger.log(Level.WARNING, String.format("Interrupted executing %s", processName), ex); //NON-NLS
Thread.currentThread().interrupt();
terminateProcess(processName, process);
/*
* Note that if the preceding call to terminateProcess() throws
* an IOException, the caller will get that exception instead of
* this InterruptedException, which is arguably preferable. If
* terminateProcess() does not throw an IOException, then its
* call to waitFor() will throw a fresh InterruptedException,
* which is fine.
*/
throw ex;
} }
logger.log(Level.WARNING, String.format("Thread running %s was interrupted before the process completed", processName), ex); if (process.isAlive() && terminator.shouldTerminateProcess()) {
Thread.currentThread().interrupt(); terminateProcess(processName, process);
} }
} while (process.isAlive());
/*
* Careful: Process.exitValue() throws an IllegalStateException if the
* process is still alive when the method is called. This code is set up
* so that the only way Process.exitValue() can be called is when it has
* not been bypassed by an exception and the preceding loop has
* terminated with Process.isAlive == false.
*/
return process.exitValue(); return process.exitValue();
} }
/** /**
* Kills a process and its children * Terminates a process and its children, waiting with a time out to try to
* ensure the process is no longer alive before returning.
* *
* @param process The parent process to kill * IMPORTANT: This method blocks while the process is running. For legacy
* API reasons, if there is an interrupt (or any other exception) the
* exception is logged instead of being thrown. Callers that need to know
* about interrupts to detect backgound task cancellation can call
* Thread.isInterrupted() or, if the thread's interrupt flag should be
* cleared, Thread.interrupted().
*
* @param process The process.
*/ */
public static void killProcess(Process process) { public static void killProcess(Process process) {
if (process == null) { String processName = process.toString();
try {
terminateProcess(processName, process);
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error occured executing %s", processName), ex); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.WARNING, String.format("Interrupted executing %s", processName), ex); //NON-NLS
Thread.currentThread().interrupt();
}
}
/**
* Terminates a process and its children, waiting with a time out to try to
* ensure the process is no longer alive before returning.
*
* @param processName The name of the process, for logging purposes.
* @param process The process.
*
* @throws IOException If an error occurs while trying to terminate
* the process.
* @throws InterruptedException If the thread running this code is
* interrupted while waiting for the process to
* terminate.
*/
private static void terminateProcess(String processName, Process process) throws IOException, InterruptedException {
if (process == null || !process.isAlive()) {
return; return;
} }
try { if (SystemUtils.IS_OS_WINDOWS) {
if (SystemUtils.IS_OS_WINDOWS) { try {
Win32Process parentProcess = new Win32Process(process); Win32Process parentProcess = new Win32Process(process);
List<Win32Process> children = parentProcess.getChildren(); List<Win32Process> children = parentProcess.getChildren();
children.stream().forEach((child) -> { children.stream().forEach((child) -> {
child.terminate(); child.terminate();
}); });
parentProcess.terminate(); parentProcess.terminate();
} else { } catch (Exception ex) {
process.destroyForcibly(); /*
* Wrap whatever exception was thrown from Windows in an
* exception that is appropriate for this API.
*/
throw new IOException(String.format("Error occured terminating %s", processName), ex); //NON-NLS
} }
} catch (Exception ex) { } else {
logger.log(Level.WARNING, "Error occurred when attempting to kill process: {0}", ex.getMessage()); // NON-NLS process.destroyForcibly();
}
if (!process.waitFor(MAX_WAIT_FOR_TERMINATION, MAX_WAIT_FOR_TERMINATION_UNITS)) {
throw new IOException(String.format("Failed to terminate %s after %d %s", processName, MAX_WAIT_FOR_TERMINATION, MAX_WAIT_FOR_TERMINATION_UNITS)); //NON-NLS
} }
} }
/* /*
* Used by deprecated methods. * Fields used by deprecated methods that require instantiation of an
* ExecUtil object.
*/ */
private Process proc = null; private Process proc = null;
private ExecUtil.StreamToStringRedirect errorStringRedirect = null; private ExecUtil.StreamToStringRedirect errorStringRedirect = null;

View File

@ -22,6 +22,8 @@ import java.nio.file.Path;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestStream;
/** /**
* Interface implemented by DataSourceProcessors in order to be supported by * Interface implemented by DataSourceProcessors in order to be supported by
@ -66,6 +68,31 @@ public interface AutoIngestDataSourceProcessor extends DataSourceProcessor {
*/ */
void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack); void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack);
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
* soon as the background task is started. The background task uses a
* callback object to signal task completion and return results. Method can
* throw an exception for a system level problem. The exception should not
* be thrown for an issue related to bad input data.
*
* @param deviceId An ASCII-printable identifier for the device
* associated with the data source that is intended
* to be unique across multiple cases (e.g., a UUID).
* @param dataSourcePath Path to the data source.
* @param settings The ingest job settings.
* @param progressMonitor Progress monitor that will be used by the
* background task to report progress.
* @param callBack Callback that will be used by the background task
* to return results.
*
* @return The new ingest stream or null if an error occurred. Errors will be handled by the callback.
*/
default IngestStream processWithIngestStream(String deviceId, Path dataSourcePath, IngestJobSettings settings, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
throw new UnsupportedOperationException("Streaming ingest not supported for this data source processor");
}
/** /**
* A custom exception for the use of AutomatedIngestDataSourceProcessor. * A custom exception for the use of AutomatedIngestDataSourceProcessor.
*/ */

View File

@ -0,0 +1,159 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Providing data for the data source analysis tab.
*/
public class DataSourceAnalysisSummary {
private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME);
private static final Set<String> EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>(Arrays.asList(
"PHONE NUMBERS",
"IP ADDRESSES",
"EMAIL ADDRESSES",
"URLS",
"CREDIT CARD NUMBERS"
));
private final SleuthkitCaseProvider provider;
/**
* Main constructor.
*/
public DataSourceAnalysisSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor.
*
* @param provider The means of obtaining a sleuthkit case.
*/
public DataSourceAnalysisSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
/**
* Gets counts for hashset hits.
*
* @param dataSource The datasource for which to identify hashset hits.
*
* @return The hashset set name with the number of hits in descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT);
}
/**
* Gets counts for keyword hits.
*
* @param dataSource The datasource for which to identify keyword hits.
*
* @return The keyword set name with the number of hits in descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_KEYWORD_HIT).stream()
// make sure we have a valid set and that that set does not belong to the set of excluded items
.filter((pair) -> pair != null && pair.getKey() != null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(pair.getKey().toUpperCase().trim()))
.collect(Collectors.toList());
}
/**
* Gets counts for interesting item hits.
*
* @param dataSource The datasource for which to identify interesting item
* hits.
*
* @return The interesting item set name with the number of hits in
* descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
}
/**
* Get counts for the artifact of the specified type.
*
* @param dataSource The datasource.
* @param keyType The attribute to use as the key type.
* @param artifactTypes The types of artifacts for which to query.
*
* @return A list of key value pairs where the key is the attribute type
* value and the value is the count of items found. This list is
* sorted by the count descending max to min.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
private List<Pair<String, Long>> getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes)
throws SleuthkitCaseProviderException, TskCoreException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
SleuthkitCase skCase = provider.get();
// get all artifacts in one list for each artifact type
for (ARTIFACT_TYPE type : artifactTypes) {
artifacts.addAll(skCase.getBlackboard().getArtifacts(type.getTypeID(), dataSource.getId()));
}
// group those based on the value of the attribute type that should serve as a key
Map<String, Long> countedKeys = artifacts.stream()
.map((art) -> {
String key = DataSourceInfoUtilities.getStringOrNull(art, keyType);
return (StringUtils.isBlank(key)) ? null : key;
})
.filter((key) -> key != null)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// sort from max to min counts
return countedKeys.entrySet().stream()
.map((e) -> Pair.of(e.getKey(), e.getValue()))
.sorted((a, b) -> -a.getValue().compareTo(b.getValue()))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="mainScrollPane" alignment="0" pref="756" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="mainScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="mainContentPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
</Border>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 452]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 452]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="hashsetHitsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="AnalysisPanel.hashsetHitsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 2]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="hashSetHitsPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="hashsetHitsTable"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="keywordHitsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="AnalysisPanel.keywordHitsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.Box$Filler" name="filler4">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 2]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="keywordHitsPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="keywordHitsTable"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler5">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="interestingItemLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="AnalysisPanel.interestingItemLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.Box$Filler" name="filler6">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 2]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="interestingItemPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="interestingItemsTable"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler3">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,209 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceAnalysisSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tab shown in data source summary displaying hash set hits, keyword hits,
* and interesting item hits within a datasource.
*/
@Messages({
"AnalysisPanel_keyColumn_title=Name",
"AnalysisPanel_countColumn_title=Count"
})
public class AnalysisPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
/**
* Default Column definitions for each table
*/
private static final List<ColumnModel<Pair<String, Long>>> DEFAULT_COLUMNS = Arrays.asList(
new ColumnModel<>(
Bundle.AnalysisPanel_keyColumn_title(),
(pair) -> new DefaultCellModel(pair.getKey()),
300
),
new ColumnModel<>(
Bundle.AnalysisPanel_countColumn_title(),
(pair) -> new DefaultCellModel(String.valueOf(pair.getValue())),
100
)
);
private final JTablePanel<Pair<String, Long>> hashsetHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final JTablePanel<Pair<String, Long>> keywordHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final JTablePanel<Pair<String, Long>> interestingItemsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final List<JTablePanel<?>> tables = Arrays.asList(
hashsetHitsTable,
keywordHitsTable,
interestingItemsTable
);
/**
* All of the components necessary for data fetch swing workers to load data
* for each table.
*/
private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents;
/**
* Creates a new DataSourceUserActivityPanel.
*/
public AnalysisPanel() {
this(new DataSourceAnalysisSummary());
}
public AnalysisPanel(DataSourceAnalysisSummary analysisData) {
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
// hashset hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getHashsetCounts(dataSource),
(result) -> hashsetHitsTable.showDataFetchResult(result)),
// keyword hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getKeywordCounts(dataSource),
(result) -> keywordHitsTable.showDataFetchResult(result)),
// interesting item hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getInterestingItemCounts(dataSource),
(result) -> interestingItemsTable.showDataFetchResult(result))
);
initComponents();
}
@Override
protected void onNewDataSource(DataSource dataSource) {
// if no data source is present or the case is not open,
// set results for tables to null.
if (dataSource == null || !Case.isCaseOpen()) {
this.dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
} else {
// set tables to display loading screen
this.tables.forEach((table) -> table.showDefaultLoadingMessage());
// create swing workers to run for each table
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
.stream()
.map((components) -> new DataFetchWorker<>(components, dataSource))
.collect(Collectors.toList());
// submit swing workers to run
submit(workers);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
javax.swing.JLabel hashsetHitsLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2));
javax.swing.JPanel hashSetHitsPanel = hashsetHitsTable;
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20));
javax.swing.JLabel keywordHitsLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2));
javax.swing.JPanel keywordHitsPanel = keywordHitsTable;
javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20));
javax.swing.JLabel interestingItemLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2));
javax.swing.JPanel interestingItemPanel = interestingItemsTable;
javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
mainContentPanel.setMaximumSize(new java.awt.Dimension(32767, 452));
mainContentPanel.setMinimumSize(new java.awt.Dimension(200, 452));
mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
org.openide.awt.Mnemonics.setLocalizedText(hashsetHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.hashsetHitsLabel.text")); // NOI18N
mainContentPanel.add(hashsetHitsLabel);
mainContentPanel.add(filler1);
hashSetHitsPanel.setAlignmentX(0.0F);
hashSetHitsPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
hashSetHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106));
hashSetHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106));
mainContentPanel.add(hashSetHitsPanel);
mainContentPanel.add(filler2);
org.openide.awt.Mnemonics.setLocalizedText(keywordHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.keywordHitsLabel.text")); // NOI18N
mainContentPanel.add(keywordHitsLabel);
mainContentPanel.add(filler4);
keywordHitsPanel.setAlignmentX(0.0F);
keywordHitsPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
keywordHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106));
keywordHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106));
mainContentPanel.add(keywordHitsPanel);
mainContentPanel.add(filler5);
org.openide.awt.Mnemonics.setLocalizedText(interestingItemLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.interestingItemLabel.text")); // NOI18N
mainContentPanel.add(interestingItemLabel);
mainContentPanel.add(filler6);
interestingItemPanel.setAlignmentX(0.0F);
interestingItemPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
interestingItemPanel.setMinimumSize(new java.awt.Dimension(10, 106));
interestingItemPanel.setPreferredSize(new java.awt.Dimension(10, 106));
mainContentPanel.add(interestingItemPanel);
mainContentPanel.add(filler3);
mainScrollPane.setViewportView(mainContentPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 756, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -35,6 +35,9 @@ DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts
DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches
DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
AnalysisPanel.hashsetHitsLabel.text=Hashset Hits
AnalysisPanel.keywordHitsLabel.text=Keyword Hits
AnalysisPanel.interestingItemLabel.text=Interesting Item Hits
RecentFilesPanel.openDocsLabel.text=Recently Opened Documents RecentFilesPanel.openDocsLabel.text=Recently Opened Documents
RecentFilesPanel.downloadLabel.text=Recent Downloads RecentFilesPanel.downloadLabel.text=Recent Downloads
RecentFilesPanel.attachmentLabel.text=Recent Attachements RecentFilesPanel.attachmentLabel.text=Recent Attachements

View File

@ -1,3 +1,5 @@
AnalysisPanel_countColumn_title=Count
AnalysisPanel_keyColumn_title=Name
CTL_DataSourceSummaryAction=Data Source Summary CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type
@ -64,6 +66,7 @@ DataSourceSummaryNode.column.status.header=Ingest Status
DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.tags.header=Tags
DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_analysisTab_title=Analysis
DataSourceSummaryTabbedPane_countsTab_title=Counts DataSourceSummaryTabbedPane_countsTab_title=Counts
DataSourceSummaryTabbedPane_detailsTab_title=Container DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
@ -74,6 +77,9 @@ DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts
DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches
DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
AnalysisPanel.hashsetHitsLabel.text=Hashset Hits
AnalysisPanel.keywordHitsLabel.text=Keyword Hits
AnalysisPanel.interestingItemLabel.text=Interesting Item Hits
DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists
DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_tab_title=User Activity
DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type

View File

@ -37,7 +37,8 @@ import org.sleuthkit.datamodel.DataSource;
"DataSourceSummaryTabbedPane_detailsTab_title=Container", "DataSourceSummaryTabbedPane_detailsTab_title=Container",
"DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity",
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History", "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History",
"DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files" "DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis"
}) })
public class DataSourceSummaryTabbedPane extends JTabbedPane { public class DataSourceSummaryTabbedPane extends JTabbedPane {
@ -47,7 +48,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
private final List<Pair<String, BaseDataSourceSummaryPanel>> tabs = new ArrayList<>(Arrays.asList( private final List<Pair<String, BaseDataSourceSummaryPanel>> tabs = new ArrayList<>(Arrays.asList(
Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()) Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel())
)); ));
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();

View File

@ -103,7 +103,8 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
return new DefaultCellModel( return new DefaultCellModel(
getShortFolderName( getShortFolderName(
prog.getProgramPath(), prog.getProgramPath(),
prog.getProgramName())); prog.getProgramName()))
.setTooltip(prog.getProgramPath());
}, },
150), 150),
// run count column // run count column

View File

@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.discovery;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListCellRenderer;
@ -204,11 +206,30 @@ final class GroupListPanel extends javax.swing.JPanel {
if (newValue instanceof GroupKey) { if (newValue instanceof GroupKey) {
String valueString = newValue.toString(); String valueString = newValue.toString();
setToolTipText(valueString); setToolTipText(valueString);
//if paths would be longer than 37 characters shorten them to be 37 characters
if (groupingAttribute instanceof FileSearch.ParentPathAttribute && valueString.length() > 37) { valueString += " (" + groupMap.get(newValue) + ")";
valueString = valueString.substring(0, 16) + " ... " + valueString.substring(valueString.length() - 16);
if (groupingAttribute instanceof FileSearch.ParentPathAttribute) {
// Using the list FontRenderContext instead of this because
// the label RenderContext was sometimes null, but this should work.
FontRenderContext context = ((Graphics2D) list.getGraphics()).getFontRenderContext();
//Determine the width of the string with the given font.
double stringWidth = getFont().getStringBounds(valueString, context).getWidth();
// subtracting 10 from the width as a littl inset.
int listWidth = list.getWidth() - 10;
if (stringWidth > listWidth) {
double avgCharWidth = Math.floor(stringWidth / valueString.length());
// The extra 5 is to account for the " ... " that is being added back.
int charToRemove = (int) Math.ceil((stringWidth - listWidth) / avgCharWidth) + 5;
int charactersToShow = (int) Math.ceil((valueString.length() - charToRemove) / 2);
valueString = valueString.substring(0, charactersToShow) + " ... " + valueString.substring(valueString.length() - charactersToShow);
}
} }
newValue = valueString + " (" + groupMap.get(newValue) + ")"; newValue = valueString;
} }
super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
return this; return this;

View File

@ -58,6 +58,11 @@ class IngestJobInputStream implements IngestStream {
} }
ingestJob.addStreamingIngestFiles(fileObjectIds); ingestJob.addStreamingIngestFiles(fileObjectIds);
} }
@Override
public IngestJob getIngestJob() {
return ingestJob;
}
@Override @Override
public synchronized void close() { public synchronized void close() {

View File

@ -34,6 +34,13 @@ public interface IngestStream {
* @throws IngestStreamClosedException * @throws IngestStreamClosedException
*/ */
void addFiles(List<Long> fileObjectIds) throws IngestStreamClosedException; void addFiles(List<Long> fileObjectIds) throws IngestStreamClosedException;
/**
* Get the ingest job associated with this ingest stream.
*
* @return The IngestJob.
*/
IngestJob getIngestJob();
/** /**
* Closes the ingest stream. Should be called after all files from data * Closes the ingest stream. Should be called after all files from data

View File

@ -275,6 +275,21 @@ final class AutoIngestJobLogger {
void logIngestJobSettingsErrors() throws AutoIngestJobLoggerException, InterruptedException { void logIngestJobSettingsErrors() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to analyze data source due to settings errors"); log(MessageCategory.ERROR, "Failed to analyze data source due to settings errors");
} }
/**
* Logs failure to analyze a data source, possibly due to ingest job settings errors.
* Used with streaming ingest since incorrect settings are the most likely cause
* of the error.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logProbableIngestJobSettingsErrors() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to analyze data source, probably due to ingest settings errors");
}
/** /**
* Logs failure to analyze a data source due to ingest module startup * Logs failure to analyze a data source due to ingest module startup

View File

@ -101,6 +101,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobStartResult; import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleError; import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleException; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleException;
import org.sleuthkit.autopsy.keywordsearch.Server; import org.sleuthkit.autopsy.keywordsearch.Server;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -165,6 +166,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
private AutoIngestJob currentJob; private AutoIngestJob currentJob;
@GuardedBy("jobsLock") @GuardedBy("jobsLock")
private List<AutoIngestJob> completedJobs; private List<AutoIngestJob> completedJobs;
private IngestStream currentIngestStream = null;
private CoordinationService coordinationService; private CoordinationService coordinationService;
private JobProcessingTask jobProcessingTask; private JobProcessingTask jobProcessingTask;
private Future<?> jobProcessingTaskFuture; private Future<?> jobProcessingTaskFuture;
@ -2443,6 +2445,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
return; return;
} }
currentIngestStream = null;
runDataSourceProcessor(caseForJob, dataSource); runDataSourceProcessor(caseForJob, dataSource);
if (dataSource.getContent().isEmpty()) { if (dataSource.getContent().isEmpty()) {
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now())); currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now()));
@ -2558,7 +2561,29 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
caseForJob.notifyAddingDataSource(taskId); caseForJob.notifyAddingDataSource(taskId);
jobLogger.logDataSourceProcessorSelected(selectedProcessor.getDataSourceType()); jobLogger.logDataSourceProcessorSelected(selectedProcessor.getDataSourceType());
sysLogger.log(Level.INFO, "Identified data source type for {0} as {1}", new Object[]{manifestPath, selectedProcessor.getDataSourceType()}); sysLogger.log(Level.INFO, "Identified data source type for {0} as {1}", new Object[]{manifestPath, selectedProcessor.getDataSourceType()});
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack); if (selectedProcessor.supportsIngestStream()) {
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
if (! ingestJobSettings.getWarnings().isEmpty()) {
for (String warning : ingestJobSettings.getWarnings()) {
sysLogger.log(Level.SEVERE, "Ingest job settings error for {0}: {1}", new Object[]{manifestPath, warning});
}
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logIngestJobSettingsErrors();
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Error(s) in ingest job settings for " + manifestPath);
}
currentIngestStream = selectedProcessor.processWithIngestStream(dataSource.getDeviceId(), dataSource.getPath(), ingestJobSettings, progressMonitor, callBack);
if (currentIngestStream == null) {
// Either there was a failure to add the data source object to the database or the ingest settings were bad.
// An error in the ingest settings is the more likely scenario.
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logProbableIngestJobSettingsErrors();
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Error initializing processing for " + manifestPath + ", probably due to an ingest settings error");
}
} else {
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
}
ingestLock.wait(); ingestLock.wait();
// at this point we got the content object(s) from the current DSP. // at this point we got the content object(s) from the current DSP.
@ -2568,6 +2593,12 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
// move onto the the next DSP that can process this data source // move onto the the next DSP that can process this data source
jobLogger.logDataSourceProcessorError(selectedProcessor.getDataSourceType()); jobLogger.logDataSourceProcessorError(selectedProcessor.getDataSourceType());
logDataSourceProcessorResult(dataSource); logDataSourceProcessorResult(dataSource);
// If we had created an ingest stream, close it
if (currentIngestStream != null) {
currentIngestStream.stop();
currentIngestStream = null;
}
continue; continue;
} }
@ -2674,69 +2705,77 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
try { try {
synchronized (ingestLock) { synchronized (ingestLock) {
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString()); IngestJob ingestJob;
List<String> settingsWarnings = ingestJobSettings.getWarnings(); IngestJobStartResult ingestJobStartResult = null;
if (settingsWarnings.isEmpty()) { if (currentIngestStream == null) {
IngestJobStartResult ingestJobStartResult = IngestManager.getInstance().beginIngestJob(dataSource.getContent(), ingestJobSettings); IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
IngestJob ingestJob = ingestJobStartResult.getJob(); List<String> settingsWarnings = ingestJobSettings.getWarnings();
if (null != ingestJob) { if (! settingsWarnings.isEmpty()) {
currentJob.setIngestJob(ingestJob); for (String warning : settingsWarnings) {
/* sysLogger.log(Level.SEVERE, "Ingest job settings error for {0}: {1}", new Object[]{manifestPath, warning});
* Block until notified by the ingest job event }
* listener or until interrupted because auto ingest currentJob.setErrorsOccurred(true);
* is shutting down. setErrorsOccurredFlagForCase(caseDirectoryPath);
*/ jobLogger.logIngestJobSettingsErrors();
ingestLock.wait(); throw new AnalysisStartupException("Error(s) in ingest job settings");
sysLogger.log(Level.INFO, "Finished ingest modules analysis for {0} ", manifestPath); }
IngestJob.ProgressSnapshot jobSnapshot = ingestJob.getSnapshot();
for (IngestJob.ProgressSnapshot.DataSourceProcessingSnapshot snapshot : jobSnapshot.getDataSourceSnapshots()) {
AutoIngestJobLogger nestedJobLogger = new AutoIngestJobLogger(manifestPath, snapshot.getDataSource(), caseDirectoryPath); ingestJobStartResult = IngestManager.getInstance().beginIngestJob(dataSource.getContent(), ingestJobSettings);
if (!snapshot.isCancelled()) { ingestJob = ingestJobStartResult.getJob();
List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules(); } else {
if (!cancelledModules.isEmpty()) { ingestJob = currentIngestStream.getIngestJob();
sysLogger.log(Level.WARNING, String.format("Ingest module(s) cancelled for %s", manifestPath)); }
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath); if (null != ingestJob) {
for (String module : snapshot.getCancelledDataSourceIngestModules()) { currentJob.setIngestJob(ingestJob);
sysLogger.log(Level.WARNING, String.format("%s ingest module cancelled for %s", module, manifestPath)); /*
nestedJobLogger.logIngestModuleCancelled(module); * Block until notified by the ingest job event
} * listener or until interrupted because auto ingest
} * is shutting down.
nestedJobLogger.logAnalysisCompleted(); */
} else { ingestLock.wait();
currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING, Date.from(Instant.now())); sysLogger.log(Level.INFO, "Finished ingest modules analysis for {0} ", manifestPath);
IngestJob.ProgressSnapshot jobSnapshot = ingestJob.getSnapshot();
for (IngestJob.ProgressSnapshot.DataSourceProcessingSnapshot snapshot : jobSnapshot.getDataSourceSnapshots()) {
AutoIngestJobLogger nestedJobLogger = new AutoIngestJobLogger(manifestPath, snapshot.getDataSource(), caseDirectoryPath);
if (!snapshot.isCancelled()) {
List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
if (!cancelledModules.isEmpty()) {
sysLogger.log(Level.WARNING, String.format("Ingest module(s) cancelled for %s", manifestPath));
currentJob.setErrorsOccurred(true); currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath); setErrorsOccurredFlagForCase(caseDirectoryPath);
nestedJobLogger.logAnalysisCancelled(); for (String module : snapshot.getCancelledDataSourceIngestModules()) {
CancellationReason cancellationReason = snapshot.getCancellationReason(); sysLogger.log(Level.WARNING, String.format("%s ingest module cancelled for %s", module, manifestPath));
if (CancellationReason.NOT_CANCELLED != cancellationReason && CancellationReason.USER_CANCELLED != cancellationReason) { nestedJobLogger.logIngestModuleCancelled(module);
throw new AnalysisStartupException(String.format("Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), manifestPath));
} }
} }
nestedJobLogger.logAnalysisCompleted();
} else {
currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING, Date.from(Instant.now()));
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
nestedJobLogger.logAnalysisCancelled();
CancellationReason cancellationReason = snapshot.getCancellationReason();
if (CancellationReason.NOT_CANCELLED != cancellationReason && CancellationReason.USER_CANCELLED != cancellationReason) {
throw new AnalysisStartupException(String.format("Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), manifestPath));
}
} }
} else if (!ingestJobStartResult.getModuleErrors().isEmpty()) {
for (IngestModuleError error : ingestJobStartResult.getModuleErrors()) {
sysLogger.log(Level.SEVERE, String.format("%s ingest module startup error for %s", error.getModuleDisplayName(), manifestPath), error.getThrowable());
}
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logIngestModuleStartupErrors();
throw new AnalysisStartupException(String.format("Error(s) during ingest module startup for %s", manifestPath));
} else {
sysLogger.log(Level.SEVERE, String.format("Ingest manager ingest job start error for %s", manifestPath), ingestJobStartResult.getStartupException());
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logAnalysisStartupError();
throw new AnalysisStartupException("Ingest manager error starting job", ingestJobStartResult.getStartupException());
} }
} else { } else if (ingestJobStartResult != null && !ingestJobStartResult.getModuleErrors().isEmpty()) {
for (String warning : settingsWarnings) { for (IngestModuleError error : ingestJobStartResult.getModuleErrors()) {
sysLogger.log(Level.SEVERE, "Ingest job settings error for {0}: {1}", new Object[]{manifestPath, warning}); sysLogger.log(Level.SEVERE, String.format("%s ingest module startup error for %s", error.getModuleDisplayName(), manifestPath), error.getThrowable());
} }
currentJob.setErrorsOccurred(true); currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath); setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logIngestJobSettingsErrors(); jobLogger.logIngestModuleStartupErrors();
throw new AnalysisStartupException("Error(s) in ingest job settings"); throw new AnalysisStartupException(String.format("Error(s) during ingest module startup for %s", manifestPath));
} else if (ingestJobStartResult != null) {
sysLogger.log(Level.SEVERE, String.format("Ingest manager ingest job start error for %s", manifestPath), ingestJobStartResult.getStartupException());
currentJob.setErrorsOccurred(true);
setErrorsOccurredFlagForCase(caseDirectoryPath);
jobLogger.logAnalysisStartupError();
throw new AnalysisStartupException("Ingest manager error starting job", ingestJobStartResult.getStartupException());
} }
} }
} finally { } finally {

View File

@ -365,7 +365,7 @@ public class GroupManager {
} else { //group == null } else { //group == null
// It may be that this was the last unanalyzed file in the group, so test // It may be that this was the last unanalyzed file in the group, so test
// whether the group is now fully analyzed. // whether the group is now fully analyzed.
return popuplateIfAnalyzed(groupKey, null); return populateIfAnalyzed(groupKey, null);
} }
} }
@ -574,7 +574,7 @@ public class GroupManager {
* 'populateIfAnalyzed' will still not return a group and therefore * 'populateIfAnalyzed' will still not return a group and therefore
* this method will never mark the group as unseen. * this method will never mark the group as unseen.
*/ */
group = popuplateIfAnalyzed(groupKey, null); group = populateIfAnalyzed(groupKey, null);
} else { } else {
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it. //if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
group.addFile(fileID); group.addFile(fileID);
@ -680,7 +680,7 @@ public class GroupManager {
} else if (groupKey.getValue().toString().equalsIgnoreCase(this.currentPathGroup.getValue().toString()) == false) { } else if (groupKey.getValue().toString().equalsIgnoreCase(this.currentPathGroup.getValue().toString()) == false) {
// mark the last path group as analyzed // mark the last path group as analyzed
getDrawableDB().markGroupAnalyzed(currentPathGroup); getDrawableDB().markGroupAnalyzed(currentPathGroup);
popuplateIfAnalyzed(currentPathGroup, null); populateIfAnalyzed(currentPathGroup, null);
currentPathGroup = groupKey; currentPathGroup = groupKey;
} }
@ -698,7 +698,7 @@ public class GroupManager {
try { try {
if (currentPathGroup != null) { if (currentPathGroup != null) {
getDrawableDB().markGroupAnalyzed(currentPathGroup); getDrawableDB().markGroupAnalyzed(currentPathGroup);
popuplateIfAnalyzed(currentPathGroup, null); populateIfAnalyzed(currentPathGroup, null);
currentPathGroup = null; currentPathGroup = null;
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -713,7 +713,7 @@ public class GroupManager {
* *
* @returns null if Group is not ready to be viewed * @returns null if Group is not ready to be viewed
*/ */
synchronized private DrawableGroup popuplateIfAnalyzed(GroupKey<?> groupKey, ReGroupTask<?> task) { synchronized private DrawableGroup populateIfAnalyzed(GroupKey<?> groupKey, ReGroupTask<?> task) {
/* /*
* If this method call is part of a ReGroupTask and that task is * If this method call is part of a ReGroupTask and that task is
* cancelled, no-op. * cancelled, no-op.
@ -735,7 +735,7 @@ public class GroupManager {
if (groupKey.getAttribute() != DrawableAttribute.PATH if (groupKey.getAttribute() != DrawableAttribute.PATH
|| getDrawableDB().isGroupAnalyzed(groupKey)) { || getDrawableDB().isGroupAnalyzed(groupKey)) {
Set<Long> fileIDs = getFileIDsInGroup(groupKey); Set<Long> fileIDs = getFileIDsInGroup(groupKey);
if (Objects.nonNull(fileIDs)) { if (Objects.nonNull(fileIDs) && ! fileIDs.isEmpty()) {
long examinerID = collaborativeModeProp.get() ? -1 : controller.getCaseDatabase().getCurrentExaminer().getId(); long examinerID = collaborativeModeProp.get() ? -1 : controller.getCaseDatabase().getCurrentExaminer().getId();
final boolean groupSeen = getDrawableDB().isGroupSeenByExaminer(groupKey, examinerID); final boolean groupSeen = getDrawableDB().isGroupSeenByExaminer(groupKey, examinerID);
@ -866,7 +866,7 @@ public class GroupManager {
p++; p++;
updateMessage(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()) + valForDataSource.getValue()); updateMessage(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()) + valForDataSource.getValue());
updateProgress(p, valsByDataSource.size()); updateProgress(p, valsByDataSource.size());
popuplateIfAnalyzed(new GroupKey<>(groupBy, valForDataSource.getValue(), valForDataSource.getKey()), this); populateIfAnalyzed(new GroupKey<>(groupBy, valForDataSource.getValue(), valForDataSource.getKey()), this);
} }
Optional<DrawableGroup> viewedGroup Optional<DrawableGroup> viewedGroup

View File

@ -49,10 +49,7 @@ ExtractRegistry.analyzeRegFiles.failedParsingResults={0}: Failed parsing registr
ExtractRegistry.parentModuleName.noSpace=RecentActivity ExtractRegistry.parentModuleName.noSpace=RecentActivity
ExtractRegistry.programName=RegRipper ExtractRegistry.programName=RegRipper
ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}: Error reading registry file - {1} ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}: Error reading registry file - {1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}: Failed to analyze registry file ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}: Failed to analyze registry file {1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile2={0}: Failed to analyze registry file
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile3={0}: Failed to analyze registry file
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile4={0}: Failed to analyze registry file
Firefox.moduleName=FireFox Firefox.moduleName=FireFox
Firefox.getHistory.errMsg.errFetchingFiles=Error fetching internet history files for Firefox. Firefox.getHistory.errMsg.errFetchingFiles=Error fetching internet history files for Firefox.
Firefox.getHistory.errMsg.noFilesFound=No FireFox history files found. Firefox.getHistory.errMsg.noFilesFound=No FireFox history files found.

View File

@ -5,10 +5,15 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis. ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s. ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
ChromeCacheExtractor.moduleName=ChromeCacheExtractor ChromeCacheExtractor.moduleName=ChromeCacheExtractor
# {0} - module name
# {1} - row number
# {2} - table length
# {3} - cache path
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_AndroidMedia=Android Media Card
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
DataSourceUsage_FlashDrive=Flash Drive DataSourceUsage_FlashDrive=Flash Drive
# {0} - OS name
DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0}) DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
DataSourceUsageAnalyzer.parentModuleName=Recent Activity DataSourceUsageAnalyzer.parentModuleName=Recent Activity
Extract.indexError.message=Failed to index artifact for keyword search. Extract.indexError.message=Failed to index artifact for keyword search.
@ -19,6 +24,8 @@ ExtractEdge_process_errMsg_errGettingWebCacheFiles=Error trying to retrieving Ed
ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan.edb file ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan.edb file
ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer
ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file
# {0} - sub module name
ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history
ExtractOs.androidOs.label=Android ExtractOs.androidOs.label=Android
ExtractOs.androidVolume.label=OS Drive (Android) ExtractOs.androidVolume.label=OS Drive (Android)
ExtractOs.debianLinuxOs.label=Linux (Debian) ExtractOs.debianLinuxOs.label=Linux (Debian)
@ -50,6 +57,8 @@ ExtractOs.windowsVolume.label=OS Drive (Windows)
ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog) ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog)
ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog) ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog)
ExtractOS_progressMessage=Checking for OS ExtractOS_progressMessage=Checking for OS
# {0} - sub module name
ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files
ExtractPrefetch_module_name=Windows Prefetch Extractor ExtractPrefetch_module_name=Windows Prefetch Extractor
ExtractRecycleBin_module_name=Recycle Bin ExtractRecycleBin_module_name=Recycle Bin
ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files. ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.
@ -122,10 +131,7 @@ ExtractRegistry.analyzeRegFiles.failedParsingResults={0}: Failed parsing registr
ExtractRegistry.parentModuleName.noSpace=RecentActivity ExtractRegistry.parentModuleName.noSpace=RecentActivity
ExtractRegistry.programName=RegRipper ExtractRegistry.programName=RegRipper
ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}: Error reading registry file - {1} ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}: Error reading registry file - {1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}: Failed to analyze registry file ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}: Failed to analyze registry file {1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile2={0}: Failed to analyze registry file
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile3={0}: Failed to analyze registry file
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile4={0}: Failed to analyze registry file
Firefox.moduleName=FireFox Firefox.moduleName=FireFox
Firefox.getHistory.errMsg.errFetchingFiles=Error fetching internet history files for Firefox. Firefox.getHistory.errMsg.errFetchingFiles=Error fetching internet history files for Firefox.
Firefox.getHistory.errMsg.noFilesFound=No FireFox history files found. Firefox.getHistory.errMsg.noFilesFound=No FireFox history files found.
@ -211,6 +217,7 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU
Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM) Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)
RegRipperFullNotFound=Full version RegRipper executable not found. RegRipperFullNotFound=Full version RegRipper executable not found.
RegRipperNotFound=Autopsy RegRipper executable not found. RegRipperNotFound=Autopsy RegRipper executable not found.
# {0} - file name
SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}.
SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine
SearchEngineURLQueryAnalyzer.engineName.none=NONE SearchEngineURLQueryAnalyzer.engineName.none=NONE

View File

@ -40,10 +40,7 @@ ExtractPrefetch_module_name=Windows Prefetch Extractor
ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb - {1}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f ExtractRegistry.analyzeRegFiles.errMsg.errReadingRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb - {1}\u3092\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb{1}\u3092\u89e3\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
ExtractRegistry.analyzeRegFiles.failedParsingResults={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u7d50\u679c\u306e\u30d1\u30fc\u30b9\u306b\u5931\u6557\u3057\u307e\u3057\u305f{1} ExtractRegistry.analyzeRegFiles.failedParsingResults={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u7d50\u679c\u306e\u30d1\u30fc\u30b9\u306b\u5931\u6557\u3057\u307e\u3057\u305f{1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f {1}
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile2={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile3={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f
ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile4={0}\:\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\u306e\u89e3\u6790\u306b\u5931\u6557\u3057\u307e\u3057\u305f
ExtractRegistry.findRegFiles.errMsg.errReadingFile=\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\uff1a{0}\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f ExtractRegistry.findRegFiles.errMsg.errReadingFile=\u30ec\u30b8\u30b9\u30c8\u30ea\u30d5\u30a1\u30a4\u30eb\uff1a{0}\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
ExtractRegistry.moduleName.text=\u30ec\u30b8\u30b9\u30c8\u30ea ExtractRegistry.moduleName.text=\u30ec\u30b8\u30b9\u30c8\u30ea
ExtractRegistry.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3 ExtractRegistry.parentModuleName.noSpace=\u6700\u8fd1\u306e\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -162,24 +162,24 @@ final class ExtractEdge extends Extract {
final String esedumper = getPathForESEDumper(); final String esedumper = getPathForESEDumper();
if (esedumper == null) { if (esedumper == null) {
this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_unableFindESEViewer());
LOG.log(Level.SEVERE, "Error finding ESEDatabaseViewer program"); //NON-NLS LOG.log(Level.SEVERE, "Error finding ESEDatabaseViewer program"); //NON-NLS
this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_unableFindESEViewer());
return; //If we cannot find the ESEDatabaseView we cannot proceed return; //If we cannot find the ESEDatabaseView we cannot proceed
} }
try { try {
this.processWebCacheDbFile(esedumper, webCacheFiles, progressBar); this.processWebCacheDbFile(esedumper, webCacheFiles, progressBar);
} catch (IOException | TskCoreException ex) { } catch (IOException | TskCoreException ex) {
LOG.log(Level.SEVERE, "Error processing 'WebCacheV01.dat' files for Microsoft Edge", ex); // NON-NLS
this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_webcacheFail()); this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_webcacheFail());
LOG.log(Level.SEVERE, "Error returned from processWebCacheDbFile", ex); // NON-NLS
} }
progressBar.progress(Bundle.Progress_Message_Edge_Bookmarks()); progressBar.progress(Bundle.Progress_Message_Edge_Bookmarks());
try { try {
this.processSpartanDbFile(esedumper, spartanFiles); this.processSpartanDbFile(esedumper, spartanFiles);
} catch (IOException | TskCoreException ex) { } catch (IOException | TskCoreException ex) {
LOG.log(Level.SEVERE, "Error processing 'spartan.edb' files for Microsoft Edge", ex); // NON-NLS
this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_spartanFail()); this.addErrorMessage(Bundle.ExtractEdge_process_errMsg_spartanFail());
LOG.log(Level.SEVERE, "Error returned from processSpartanDbFile", ex); // NON-NLS
} }
} }
@ -584,7 +584,7 @@ final class ExtractEdge extends Extract {
processBuilder.redirectOutput(outputFilePath.toFile()); processBuilder.redirectOutput(outputFilePath.toFile());
processBuilder.redirectError(errFilePath.toFile()); processBuilder.redirectError(errFilePath.toFile());
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
} }
/** /**

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* *
* Copyright 2012 42six Solutions. * Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com * Contact: aebadirad <at> 42six <dot> com
@ -46,7 +46,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -56,7 +55,9 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.*; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Extracts activity from Internet Explorer browser, as well as recent documents * Extracts activity from Internet Explorer browser, as well as recent documents
@ -65,7 +66,6 @@ import org.sleuthkit.datamodel.*;
class ExtractIE extends Extract { class ExtractIE extends Extract {
private static final Logger logger = Logger.getLogger(ExtractIE.class.getName()); private static final Logger logger = Logger.getLogger(ExtractIE.class.getName());
private final IngestServices services = IngestServices.getInstance();
private final String moduleTempResultsDir; private final String moduleTempResultsDir;
private String PASCO_LIB_PATH; private String PASCO_LIB_PATH;
private final String JAVA_PATH; private final String JAVA_PATH;
@ -387,6 +387,10 @@ class ExtractIE extends Extract {
* *
* @return false on error * @return false on error
*/ */
@Messages({
"# {0} - sub module name",
"ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history",
})
private boolean executePasco(String indexFilePath, String outputFileName) { private boolean executePasco(String indexFilePath, String outputFileName) {
boolean success = true; boolean success = true;
try { try {
@ -413,11 +417,12 @@ class ExtractIE extends Extract {
* contains a lot of useful data and only the last entry is * contains a lot of useful data and only the last entry is
* corrupted. * corrupted.
*/ */
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
// @@@ Investigate use of history versus cache as type. // @@@ Investigate use of history versus cache as type.
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, "Error executing Pasco to process Internet Explorer web history", ex); //NON-NLS
addErrorMessage(Bundle.ExtractIE_executePasco_errMsg_errorRunningPasco(getName()));
success = false; success = false;
logger.log(Level.SEVERE, "Unable to execute Pasco to process Internet Explorer web history.", ex); //NON-NLS
} }
return success; return success;
} }

View File

@ -78,7 +78,9 @@ final class ExtractPrefetch extends Extract {
private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS private static final String PREFETCH_DIR_NAME = "prefetch"; //NON-NLS
@Messages({ @Messages({
"ExtractPrefetch_module_name=Windows Prefetch Extractor" "ExtractPrefetch_module_name=Windows Prefetch Extractor",
"# {0} - sub module name",
"ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files"
}) })
ExtractPrefetch() { ExtractPrefetch() {
this.moduleName = Bundle.ExtractPrefetch_module_name(); this.moduleName = Bundle.ExtractPrefetch_module_name();
@ -96,7 +98,6 @@ final class ExtractPrefetch extends Extract {
if (!dirMade) { if (!dirMade) {
logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS logger.log(Level.SEVERE, "Error creating directory to store prefetch output database"); //NON-NLS
return; //If we cannot create the directory then we need to exit return; //If we cannot create the directory then we need to exit
} }
} }
@ -118,7 +119,8 @@ final class ExtractPrefetch extends Extract {
parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath); parsePrefetchFiles(prefetchDumper, tempDirPath, modOutFile, modOutPath);
createAppExecArtifacts(modOutFile, dataSource); createAppExecArtifacts(modOutFile, dataSource);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Error runing parse_prefetch or creating artifacts.", ex); //NON-NLS logger.log(Level.SEVERE, "Error parsing prefetch files", ex); //NON-NLS
addErrorMessage(Bundle.ExtractPrefetch_errMsg_prefetchParsingFailed(Bundle.ExtractPrefetch_module_name()));
} }
} }
@ -127,7 +129,6 @@ final class ExtractPrefetch extends Extract {
* that the prefetch files only come from the /Windows/Prefetch directory * that the prefetch files only come from the /Windows/Prefetch directory
* *
* @param dataSource - datasource to search for prefetch files * @param dataSource - datasource to search for prefetch files
*
*/ */
void extractPrefetchFiles(Content dataSource) { void extractPrefetchFiles(Content dataSource) {
List<AbstractFile> pFiles; List<AbstractFile> pFiles;
@ -184,7 +185,7 @@ final class ExtractPrefetch extends Extract {
processBuilder.redirectOutput(outputFilePath.toFile()); processBuilder.redirectOutput(outputFilePath.toFile());
processBuilder.redirectError(errFilePath.toFile()); processBuilder.redirectError(errFilePath.toFile());
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
} }
/** /**

View File

@ -443,8 +443,8 @@ class ExtractRegistry extends Extract {
try { try {
scanErrorLogs(errFilePath); scanErrorLogs(errFilePath);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS logger.log(Level.SEVERE, String.format("Unable to run RegRipper on %s", regFilePath), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName(), regFilePath));
} }
} }
return regOutputFiles; return regOutputFiles;
@ -480,10 +480,10 @@ class ExtractRegistry extends Extract {
processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory
processBuilder.redirectOutput(new File(outputFile)); processBuilder.redirectOutput(new File(outputFile));
processBuilder.redirectError(new File(errFile)); processBuilder.redirectError(new File(errFile));
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS logger.log(Level.SEVERE, String.format("Error running RegRipper on %s", hiveFilePath), ex); //NON-NLS
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName(), hiveFilePath));
} }
} }

View File

@ -138,8 +138,8 @@ final class ExtractSru extends Extract {
createNetUsageArtifacts(modOutFile, sruAbstractFile); createNetUsageArtifacts(modOutFile, sruAbstractFile);
createAppUsageArtifacts(modOutFile, sruAbstractFile); createAppUsageArtifacts(modOutFile, sruAbstractFile);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, "Error processing SRUDB.dat file", ex); //NON-NLS=
this.addErrorMessage(Bundle.ExtractSru_process_error_executing_export_srudb_program()); this.addErrorMessage(Bundle.ExtractSru_process_error_executing_export_srudb_program());
logger.log(Level.SEVERE, "SRUDB.dat file not found"); //NON-NLS
} }
} }
@ -256,7 +256,7 @@ final class ExtractSru extends Extract {
processBuilder.redirectOutput(outputFilePath.toFile()); processBuilder.redirectOutput(outputFilePath.toFile());
processBuilder.redirectError(errFilePath.toFile()); processBuilder.redirectError(errFilePath.toFile());
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context, true));
} }
private String getPathForSruDumper() { private String getPathForSruDumper() {

View File

@ -12,7 +12,7 @@ Each splitToken contains a single mapping of a raw URL substring to its regex eq
SearchEngine: SearchEngine:
engine: The engines basic name engine: The engines basic name
domainSubstring: The domain of the URL such that it can uniquely be identified as given engine. domainSubstring: The domain of the URL such that it can be identified as given engine. Should not have leading or trailing '.'
splitToken: splitToken:
plainToken: The string in the URL that is immediately followed by the actual query. plainToken: The string in the URL that is immediately followed by the actual query.
@ -25,30 +25,30 @@ splitToken:
--> -->
<SES> <SES>
<SearchEngine engine="Google" domainSubstring=".google."> <SearchEngine engine="Google" domainSubstring="google">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
<splitToken plainToken="&amp;q=" regexToken="&amp;q="/> <splitToken plainToken="&amp;q=" regexToken="&amp;q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Yahoo" domainSubstring=".yahoo."> <SearchEngine engine="Yahoo" domainSubstring="yahoo">
<splitToken plainToken="?p=" regexToken="\\?p="/> <splitToken plainToken="?p=" regexToken="\\?p="/>
<splitToken plainToken="?text=" regexToken="\\?text="/> <splitToken plainToken="?text=" regexToken="\\?text="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Twitter" domainSubstring="twitter."> <SearchEngine engine="Twitter" domainSubstring="twitter">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
<splitToken plainToken="&amp;q=" regexToken="&amp;q="/> <splitToken plainToken="&amp;q=" regexToken="&amp;q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="LinkedIn" domainSubstring=".linkedin."> <SearchEngine engine="LinkedIn" domainSubstring="linkedin">
<splitToken plainToken="&amp;keywords=" regexToken="&amp;keywords="/> <splitToken plainToken="&amp;keywords=" regexToken="&amp;keywords="/>
<splitToken plainToken="?keywords=" regexToken="\\?keywords="/> <splitToken plainToken="?keywords=" regexToken="\\?keywords="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Facebook" domainSubstring=".facebook."> <SearchEngine engine="Facebook" domainSubstring="facebook">
<splitToken plainToken="?value=" regexToken="\\?value="/> <splitToken plainToken="?value=" regexToken="\\?value="/>
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Bing" domainSubstring=".bing."> <SearchEngine engine="Bing" domainSubstring="bing">
<splitToken plainToken="search?q=" regexToken="search\\?q="/> <splitToken plainToken="search?q=" regexToken="search\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Baidu" domainSubstring=".baidu."> <SearchEngine engine="Baidu" domainSubstring="baidu">
<splitToken plainToken="?wd=" regexToken="\\?wd="/> <splitToken plainToken="?wd=" regexToken="\\?wd="/>
<splitToken plainToken="?kw=" regexToken="\\?kw="/> <splitToken plainToken="?kw=" regexToken="\\?kw="/>
<splitToken plainToken="baidu.com/q?" regexToken="word="/> <splitToken plainToken="baidu.com/q?" regexToken="word="/>
@ -59,28 +59,28 @@ splitToken:
<splitToken plainToken="bs=" regexToken="&amp;bs="/> <splitToken plainToken="bs=" regexToken="&amp;bs="/>
<splitToken plainToken="?ie=" regexToken="\\?ie="/> <splitToken plainToken="?ie=" regexToken="\\?ie="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Sogou" domainSubstring=".sogou.com"> <SearchEngine engine="Sogou" domainSubstring="sogou.com">
<splitToken plainToken="query=" regexToken="query="/> <splitToken plainToken="query=" regexToken="query="/>
<splitToken plainToken="?ie=" regexToken="\\?ie="/> <splitToken plainToken="?ie=" regexToken="\\?ie="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Soso" domainSubstring=".soso.com"> <SearchEngine engine="Soso" domainSubstring="soso.com">
<splitToken plainToken="p=S" regexToken="p=S"/> <splitToken plainToken="p=S" regexToken="p=S"/>
<splitToken plainToken="?w=" regexToken="\\?w="/> <splitToken plainToken="?w=" regexToken="\\?w="/>
<splitToken plainToken="&amp;w" regexToken="&amp;w"/> <splitToken plainToken="&amp;w" regexToken="&amp;w"/>
<splitToken plainToken="?ie=" regexToken="\\?ie="/> <splitToken plainToken="?ie=" regexToken="\\?ie="/>
<splitToken plainToken="&amp;query=" regexToken="&amp;query="/> <splitToken plainToken="&amp;query=" regexToken="&amp;query="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Youdao" domainSubstring="youdao."> <SearchEngine engine="Youdao" domainSubstring="youdao">
<splitToken plainToken="search?q=" regexToken="\\?q="/> <splitToken plainToken="search?q=" regexToken="\\?q="/>
<splitToken plainToken="?i=" regexToken="\\?i="/> <splitToken plainToken="?i=" regexToken="\\?i="/>
<splitToken plainToken="#keyfrom=" regexToken="\\#keyfrom="/> <splitToken plainToken="#keyfrom=" regexToken="\\#keyfrom="/>
<splitToken plainToken="?spc=" regexToken="\\?spc=" /> <splitToken plainToken="?spc=" regexToken="\\?spc=" />
</SearchEngine> </SearchEngine>
<SearchEngine engine="Yandex" domainSubstring="yandex."> <SearchEngine engine="Yandex" domainSubstring="yandex">
<splitToken plainToken="?text=" regexToken="\\?text="/> <splitToken plainToken="?text=" regexToken="\\?text="/>
<splitToken plainToken="&amp;text=" regexToken="&amp;text"/> <splitToken plainToken="&amp;text=" regexToken="&amp;text"/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Biglobe" domainSubstring=".biglobe."> <SearchEngine engine="Biglobe" domainSubstring="biglobe">
<splitToken plainToken="?search=" regexToken="\\?search="/> <splitToken plainToken="?search=" regexToken="\\?search="/>
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
<splitToken plainToken="/key/" regexToken="/key/"/> <splitToken plainToken="/key/" regexToken="/key/"/>
@ -92,22 +92,22 @@ splitToken:
<SearchEngine engine="Parseek" domainSubstring="parseek.com"> <SearchEngine engine="Parseek" domainSubstring="parseek.com">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Parset" domainSubstring=".parset.com"> <SearchEngine engine="Parset" domainSubstring="parset.com">
<splitToken plainToken="?Keyword=" regexToken="\\?Keyword="/> <splitToken plainToken="?Keyword=" regexToken="\\?Keyword="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Amazon" domainSubstring=".amazon.com"> <SearchEngine engine="Amazon" domainSubstring="amazon.com">
<splitToken plainToken="?k=" regexToken="\\?k="/> <splitToken plainToken="?k=" regexToken="\\?k="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="DuckDuckGo" domainSubstring="duckduckgo.com"> <SearchEngine engine="DuckDuckGo" domainSubstring="duckduckgo.com">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Youtube" domainSubstring=".youtube.com"> <SearchEngine engine="Youtube" domainSubstring="youtube.com">
<splitToken plainToken="?search_query=" regexToken="\\?search_query="/> <splitToken plainToken="?search_query=" regexToken="\\?search_query="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Reddit" domainSubstring=".reddit.com"> <SearchEngine engine="Reddit" domainSubstring="reddit.com">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="CCSearch" domainSubstring="ccsearch.creativecommons."> <SearchEngine engine="CCSearch" domainSubstring="ccsearch.creativecommons">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Swisscows" domainSubstring="swisscows.com"> <SearchEngine engine="Swisscows" domainSubstring="swisscows.com">
@ -116,28 +116,28 @@ splitToken:
<SearchEngine engine="GIbiru" domainSubstring="gibiru.com"> <SearchEngine engine="GIbiru" domainSubstring="gibiru.com">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Wiki" domainSubstring=".wiki."> <SearchEngine engine="Wiki" domainSubstring="wiki">
<splitToken plainToken="?search=" regexToken="\\?search="/> <splitToken plainToken="?search=" regexToken="\\?search="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Cutestat" domainSubstring=".cutestat.com"> <SearchEngine engine="Cutestat" domainSubstring="cutestat.com">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Givewater" domainSubstring="search.givewater."> <SearchEngine engine="Givewater" domainSubstring="search.givewater">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Ekoru" domainSubstring=".ekoru."> <SearchEngine engine="Ekoru" domainSubstring="ekoru">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Ecosia" domainSubstring=".ecosia."> <SearchEngine engine="Ecosia" domainSubstring="ecosia">
<splitToken plainToken="?q=" regexToken="\\?q="/> <splitToken plainToken="?q=" regexToken="\\?q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Slideshare" domainSubstring=".slideshare."> <SearchEngine engine="Slideshare" domainSubstring="slideshare">
<splitToken plainToken="&amp;q=" regexToken="&amp;q="/> <splitToken plainToken="&amp;q=" regexToken="&amp;q="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Wikipedia" domainSubstring=".wikipedia.org"> <SearchEngine engine="Wikipedia" domainSubstring="wikipedia.org">
<splitToken plainToken="?search=" regexToken="\\?search="/> <splitToken plainToken="?search=" regexToken="\\?search="/>
</SearchEngine> </SearchEngine>
<SearchEngine engine="Wiki" domainSubstring=".wiki.com"> <SearchEngine engine="Wiki" domainSubstring="wiki.com">
<splitToken plainToken="?cx=" regexToken="\\?cx="/> <splitToken plainToken="?cx=" regexToken="\\?cx="/>
</SearchEngine> </SearchEngine>
</SES> </SES>

View File

@ -22,10 +22,15 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -54,7 +59,7 @@ import org.xml.sax.SAXException;
* artifacts, and extracting search text from them. * artifacts, and extracting search text from them.
* *
* *
* To add search engines, edit SearchEngines.xml under RecentActivity * To add search engines, edit SEUQAMappings.xml under RecentActivity
* *
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
@ -107,11 +112,13 @@ class SearchEngineURLQueryAnalyzer extends Extract {
private final String engineName; private final String engineName;
private final String domainSubstring; private final String domainSubstring;
private final List<KeyPair> keyPairs; private final List<KeyPair> keyPairs;
private final Pattern domainRegexPattern;
private int count; private int count;
SearchEngine(String engineName, String domainSubstring, List<KeyPair> keyPairs) { SearchEngine(String engineName, String domainSubstring, List<KeyPair> keyPairs) {
this.engineName = engineName; this.engineName = engineName;
this.domainSubstring = domainSubstring; this.domainSubstring = domainSubstring;
domainRegexPattern = Pattern.compile("^(.*[./])?" + domainSubstring + "([./].*)?$");
this.keyPairs = keyPairs; this.keyPairs = keyPairs;
count = 0; count = 0;
} }
@ -127,6 +134,10 @@ class SearchEngineURLQueryAnalyzer extends Extract {
String getDomainSubstring() { String getDomainSubstring() {
return domainSubstring; return domainSubstring;
} }
Pattern getDomainRegexPattern() {
return domainRegexPattern;
}
int getTotal() { int getTotal() {
return count; return count;
@ -202,20 +213,21 @@ class SearchEngineURLQueryAnalyzer extends Extract {
* *
* @param domain domain as part of the URL * @param domain domain as part of the URL
* *
* @return supported search engine the domain belongs to or null if no match * @return supported search engine(s) the domain belongs to (list may be empty)
* is found
* *
*/ */
private static SearchEngineURLQueryAnalyzer.SearchEngine getSearchEngineFromUrl(String domain) { private static Collection<SearchEngineURLQueryAnalyzer.SearchEngine> getSearchEngineFromUrl(String domain) {
List<SearchEngineURLQueryAnalyzer.SearchEngine> supportedEngines = new ArrayList<>();
if (engines == null) { if (engines == null) {
return null; return supportedEngines;
} }
for (SearchEngine engine : engines) { for (SearchEngine engine : engines) {
if (domain.contains(engine.getDomainSubstring())) { Matcher matcher = engine.getDomainRegexPattern().matcher(domain);
return engine; if (matcher.matches()) {
supportedEngines.add(engine);
} }
} }
return null; return supportedEngines;
} }
/** /**
@ -294,8 +306,9 @@ class SearchEngineURLQueryAnalyzer extends Extract {
int totalQueries = 0; int totalQueries = 0;
try { try {
//from blackboard_artifacts //from blackboard_artifacts
Collection<BlackboardArtifact> listArtifacts = currentCase.getSleuthkitCase().getMatchingArtifacts("WHERE (blackboard_artifacts.artifact_type_id = '" + ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() //NON-NLS Collection<BlackboardArtifact> listArtifacts = currentCase.getSleuthkitCase().getBlackboard().getArtifacts(
+ "' OR blackboard_artifacts.artifact_type_id = '" + ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() + "') "); //List of every 'web_history' and 'bookmark' artifact NON-NLS Arrays.asList(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_BOOKMARK), new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY)),
Arrays.asList(dataSource.getId()));
logger.log(Level.INFO, "Processing {0} blackboard artifacts.", listArtifacts.size()); //NON-NLS logger.log(Level.INFO, "Processing {0} blackboard artifacts.", listArtifacts.size()); //NON-NLS
for (BlackboardArtifact artifact : listArtifacts) { for (BlackboardArtifact artifact : listArtifacts) {
@ -304,51 +317,54 @@ class SearchEngineURLQueryAnalyzer extends Extract {
} }
//initializing default attributes //initializing default attributes
String query = "";
String searchEngineDomain = ""; String searchEngineDomain = "";
String browser = ""; String browser = "";
long last_accessed = -1; long last_accessed = -1;
long fileId = artifact.getObjectID(); AbstractFile file = tskCase.getAbstractFileById(artifact.getObjectID());
boolean isFromSource = tskCase.isFileFromSource(dataSource, fileId);
if (!isFromSource) {
//File was from a different dataSource. Skipping.
continue;
}
AbstractFile file = tskCase.getAbstractFileById(fileId);
if (file == null) { if (file == null) {
continue; continue;
} }
SearchEngineURLQueryAnalyzer.SearchEngine se = null; // Try search engines on the URL to see if any produce a search string
//from blackboard_attributes Set<String> searchQueries = new HashSet<>();
Collection<BlackboardAttribute> listAttributes = currentCase.getSleuthkitCase().getMatchingAttributes("WHERE artifact_id = " + artifact.getArtifactID()); //NON-NLS BlackboardAttribute urlAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL));
if (urlAttr == null) {
for (BlackboardAttribute attribute : listAttributes) { continue;
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID()) { }
final String urlString = attribute.getValueString();
se = getSearchEngineFromUrl(urlString); final String urlString = urlAttr.getValueString();
if (se == null) { Collection<SearchEngineURLQueryAnalyzer.SearchEngine> possibleSearchEngines = getSearchEngineFromUrl(urlString);
break; for (SearchEngineURLQueryAnalyzer.SearchEngine se : possibleSearchEngines) {
} String query = extractSearchEngineQuery(se, urlString);
// If we have a non-empty query string, add it to the list
query = extractSearchEngineQuery(se, attribute.getValueString()); if ( !query.equals("")) {
if (query.equals("")) //False positive match, artifact was not a query. NON-NLS searchQueries.add(query);
{ se.increment();
break;
}
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()) {
browser = attribute.getValueString();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID()) {
searchEngineDomain = attribute.getValueString();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
last_accessed = attribute.getValueLong();
} }
} }
// If we didn't extract any search queries, go on to the next artifact
if (searchQueries.isEmpty()) {
continue;
}
// Extract the rest of the fields needed for the web search artifact
BlackboardAttribute browserAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
if (browserAttr != null) {
browser = browserAttr.getValueString();
}
BlackboardAttribute domainAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
if (domainAttr != null) {
searchEngineDomain = domainAttr.getValueString();
}
BlackboardAttribute lastAccessAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED));
if (lastAccessAttr != null) {
last_accessed = lastAccessAttr.getValueLong();
}
if (se != null && !query.equals("")) { //NON-NLS // Make an artifact for each distinct query
for (String query : searchQueries) {
// If date doesn't exist, change to 0 (instead of 1969) // If date doesn't exist, change to 0 (instead of 1969)
if (last_accessed == -1) { if (last_accessed == -1) {
last_accessed = 0; last_accessed = 0;
@ -367,7 +383,6 @@ class SearchEngineURLQueryAnalyzer extends Extract {
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"SearchEngineURLQueryAnalyzer.parentModuleName"), last_accessed)); "SearchEngineURLQueryAnalyzer.parentModuleName"), last_accessed));
postArtifact(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY, file, bbattributes)); postArtifact(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY, file, bbattributes));
se.increment();
++totalQueries; ++totalQueries;
} }
} }