diff --git a/Core/build.xml b/Core/build.xml index d246edaa54..2c461a5017 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -103,6 +103,11 @@ + + + + + diff --git a/Core/ivy.xml b/Core/ivy.xml index 1ed71d69cf..c60161dd90 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -7,12 +7,8 @@ - - - - @@ -27,6 +23,7 @@ + @@ -34,5 +31,7 @@ + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 90ce31dab9..335ff4fbc3 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -40,6 +40,7 @@ file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar +file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 303d14422c..d5d64228b6 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -356,6 +356,10 @@ ext/cxf-rt-transports-http-3.0.16.jar release/modules/ext/cxf-rt-transports-http-3.0.16.jar + + ext/commons-validator-1.6.jar + release/modules/ext/commons-validator-1.6.jar + ext/curator-framework-2.8.0.jar release/modules/ext/curator-framework-2.8.0.jar diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 9019ae162d..dcb2af3eef 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -71,12 +71,13 @@ import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.services.Services; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesSearchAction; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchAction; import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; @@ -372,7 +373,12 @@ public class Case { * old value of the PropertyChangeEvent is the display name of the tag * definition that has changed. */ - TAG_DEFINITION_CHANGED; + TAG_DEFINITION_CHANGED, + /** + * An item in the central repository has had its comment modified. The + * old value is null, the new value is string for current comment. + */ + CR_COMMENT_CHANGED; }; @@ -1087,7 +1093,7 @@ public class Case { CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true); - CallableSystemAction.get(CommonFilesSearchAction.class).setEnabled(true); + CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); /* @@ -1100,7 +1106,7 @@ public class Case { /* * Open the top components (windows within the main application * window). - * + * * Note: If the core windows are not opened here, they will be * opened via the DirectoryTreeTopComponent 'propertyChange()' * method on a DATA_SOURCE_ADDED event. @@ -1141,7 +1147,7 @@ public class Case { CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); - CallableSystemAction.get(CommonFilesSearchAction.class).setEnabled(false); + CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false); /* * Clear the notifications in the notfier component in the lower @@ -1533,6 +1539,22 @@ public class Case { eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null)); } + /** + * Notifies case event subscribers that a central repository comment has been changed. + * + * This should not be called from the event dispatch thread (EDT) + * + * @param contentId the objectId for the Content which has had its central repo comment changed + * @param newComment the new value of the comment + */ + public void notifyCentralRepoCommentChanged(long contentId, String newComment) { + try { + eventPublisher.publish(new CommentChangedEvent(contentId, newComment)); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex); + } + } + /** * Notifies case event subscribers that an artifact tag has been added. * diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index c7fa2c3de3..8d6dcffdd9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -197,7 +197,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat command.add("-f"); command.add("files"); command.add("-t"); - File l01Dir = new File(Case.getCurrentCaseThrows().getModuleDirectory(), L01_EXTRACTION_DIR); //WJS-TODO change to getOpenCase() when that method exists + File l01Dir = new File(Case.getCurrentCaseThrows().getModuleDirectory(), L01_EXTRACTION_DIR); if (!l01Dir.exists()) { l01Dir.mkdirs(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/CommentChangedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/CommentChangedEvent.java new file mode 100644 index 0000000000..8cfb8ace0d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/CommentChangedEvent.java @@ -0,0 +1,60 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule.events; + +import java.io.Serializable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +/** + * Event published when a central repsoitory comment is changed + */ +public class CommentChangedEvent extends AutopsyEvent implements Serializable { + + private static final long serialVersionUID = 1L; + + private final long contentID; + private final String caseName; //included so we can eventually use this event between cases + + /** + * Constructs a CommentChangedEvent which is published when a central + * repository comment is changed. + * + * @param contentId the objectId of the Content which has had its central repository comment changed + * @param newComment the new value of the comment + * @throws org.sleuthkit.autopsy.casemodule.NoCurrentCaseException if there is no current case open when this event is sent + */ + public CommentChangedEvent(long contentId, String newComment) throws NoCurrentCaseException { + super(Case.Events.CR_COMMENT_CHANGED.toString(), null, newComment); + contentID = contentId; + //get the caseName as well so that this event could be used for notifications between cases without method signature change + caseName = Case.getCurrentCaseThrows().getName(); + + } + + /** + * Get the object id of the content which this event is associated with. + * + * @return the objectId of the content this event is associated with + */ + public long getContentID() { + return contentID; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java index bdbf6a60b0..0698992fa7 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java @@ -24,6 +24,8 @@ import javax.swing.AbstractAction; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; @@ -35,38 +37,35 @@ import org.sleuthkit.datamodel.AbstractFile; * An AbstractAction to manage adding and modifying a Central Repository file * instance comment. */ + + @Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"}) public final class AddEditCentralRepoCommentAction extends AbstractAction { private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName()); + private static final long serialVersionUID = 1L; private boolean addToDatabase; private CorrelationAttributeInstance correlationAttributeInstance; private String comment; - - /** - * Constructor to create an instance given a CorrelationAttribute. - * - * @param correlationAttribute The correlation attribute to modify. - */ - public AddEditCentralRepoCommentAction(CorrelationAttributeInstance correlationAttribute) { - super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment()); - this.correlationAttributeInstance = correlationAttribute; - } + private final Long fileId; /** * Constructor to create an instance given an AbstractFile. * * @param file The file from which a correlation attribute to modify is * derived. + * */ public AddEditCentralRepoCommentAction(AbstractFile file) { super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment()); + fileId = file.getId(); correlationAttributeInstance = EamArtifactUtil.getInstanceFromContent(file); if (correlationAttributeInstance == null) { addToDatabase = true; correlationAttributeInstance = EamArtifactUtil.makeInstanceFromContent(file); } + } /** @@ -100,6 +99,11 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { } comment = centralRepoCommentDialog.getComment(); + try { + Case.getCurrentCaseThrows().notifyCentralRepoCommentChanged(fileId, comment); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Case not open after changing central repository comment", ex); + } } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error adding comment", ex); NotifyDescriptor notifyDescriptor = new NotifyDescriptor.Message( @@ -120,10 +124,10 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { public String getComment() { return comment; } - + /** * Retrieve the associated correlation attribute. - * + * * @return The correlation attribute. */ public CorrelationAttributeInstance getCorrelationAttribute() { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties index acb3fa42d1..2a9cd7b456 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties @@ -3,7 +3,6 @@ DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options. DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency -DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Central Repository Comment DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date DataContentViewerOtherCases.earliestCaseLabel.toolTipText= DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date: diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 9c42be16a8..1f1e497aa7 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -39,13 +39,6 @@ - - - - - - - @@ -80,7 +73,7 @@ - + @@ -106,7 +99,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 9da0b577b3..c6301bfb89 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -56,7 +56,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; @@ -79,14 +79,14 @@ import org.sleuthkit.datamodel.TskData; * View correlation results from other cases */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -@ServiceProvider(service = DataContentViewer.class, position = 8) +@ServiceProvider(service = DataContentViewer.class, position = 9) @Messages({"DataContentViewerOtherCases.title=Other Occurrences", "DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.",}) public class DataContentViewerOtherCases extends JPanel implements DataContentViewer { private static final long serialVersionUID = -1L; - private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + private static final Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private static final int DEFAULT_MIN_CELL_WIDTH = 15; private static final int CELL_TEXT_WIDTH_PADDING = 5; @@ -123,24 +123,11 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi try { saveToCSV(); } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } } else if (jmi.equals(showCommonalityMenuItem)) { showCommonalityDetails(); - } else if (jmi.equals(addCommentMenuItem)) { - try { - OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow()); - AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.getCorrelationAttribute()); - action.actionPerformed(null); - String currentComment = action.getComment(); - if (currentComment != null) { - selectedNode.updateComment(action.getComment()); - otherCasesTable.repaint(); - } - } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error performing Add/Edit Central Repository Comment action", ex); - } - } + } } }; @@ -148,7 +135,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi selectAllMenuItem.addActionListener(actList); showCaseDetailsMenuItem.addActionListener(actList); showCommonalityMenuItem.addActionListener(actList); - addCommentMenuItem.addActionListener(actList); // Set background of every nth row as light grey. TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); @@ -179,17 +165,21 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi try { EamDb dbManager = EamDb.getInstance(); for (CorrelationAttributeInstance eamArtifact : correlationAttributes) { - percentage = dbManager.getFrequencyPercentage(eamArtifact); - msg.append(Bundle.DataContentViewerOtherCases_correlatedArtifacts_byType(percentage, - eamArtifact.getCorrelationType().getDisplayName(), - eamArtifact.getCorrelationValue())); + try { + percentage = dbManager.getFrequencyPercentage(eamArtifact); + msg.append(Bundle.DataContentViewerOtherCases_correlatedArtifacts_byType(percentage, + eamArtifact.getCorrelationType().getDisplayName(), + eamArtifact.getCorrelationValue())); + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.WARNING, String.format("Error getting commonality details for artifact with ID: %s.", eamArtifact.getID()), ex); + } } JOptionPane.showConfirmDialog(showCommonalityMenuItem, msg.toString(), Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), DEFAULT_OPTION, PLAIN_MESSAGE); } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error getting commonality details.", ex); + LOGGER.log(Level.SEVERE, "Error getting commonality details.", ex); JOptionPane.showConfirmDialog(showCommonalityMenuItem, Bundle.DataContentViewerOtherCases_correlatedArtifacts_failed(), Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), @@ -242,7 +232,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi DEFAULT_OPTION, PLAIN_MESSAGE); } } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error loading case details", ex); + LOGGER.log(Level.SEVERE, "Error loading case details", ex); JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(), caseDisplayName, @@ -305,7 +295,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } } catch (IOException ex) { - logger.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); + LOGGER.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); } } @@ -397,7 +387,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi try { content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID()); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS return null; } @@ -427,9 +417,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (bbArtifact != null && EamDb.isEnabled()) { ret.addAll(EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false)); } - + // we can correlate based on the MD5 if it is enabled - if (this.file != null && EamDb.isEnabled()) { + if (this.file != null && EamDb.isEnabled()) { try { List artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); @@ -438,33 +428,47 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi for (CorrelationAttributeInstance.Type aType : artifactTypes) { if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { CorrelationCase corCase = EamDb.getInstance().getCase(Case.getCurrentCase()); - ret.add(new CorrelationAttributeInstance( - md5, - aType, - corCase, - CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), - file.getParentPath() + file.getName(), - "", - file.getKnown())); + try { + ret.add(new CorrelationAttributeInstance( + md5, + aType, + corCase, + CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), + file.getParentPath() + file.getName(), + "", + file.getKnown())); + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex); + } break; } } } } catch (EamDbException | TskCoreException ex) { - logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS - } + LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + } } else { - try { - // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. - if (this.file != null) { - String md5 = this.file.getMd5Hash(); - if (md5 != null && !md5.isEmpty()) { - ret.add(new CorrelationAttributeInstance(CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0), md5)); + + // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. + if (this.file != null) { + String md5 = this.file.getMd5Hash(); + if (md5 != null && !md5.isEmpty()) { + try { + final CorrelationAttributeInstance.Type fileAttributeType + = CorrelationAttributeInstance.getDefaultCorrelationTypes() + .stream() + .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) + .findAny() + .get(); + + ret.add(new CorrelationAttributeInstance(fileAttributeType, md5)); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS } } - } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS } } @@ -496,9 +500,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS } catch (ParseException ex) { - logger.log(Level.SEVERE, "Error parsing date of cases from database.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error parsing date of cases from database.", ex); // NON-NLS } } @@ -511,9 +515,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * artifact. If the central repo is not enabled, this will only return files * from the current case with matching MD5 hashes. * - * @param corAttr CorrelationAttribute to query for + * @param corAttr CorrelationAttribute to query for * @param dataSourceName Data source to filter results - * @param deviceId Device Id to filter results + * @param deviceId Device Id to filter results * * @return A collection of correlated artifact instances */ @@ -558,13 +562,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi return nodeDataMap; } catch (EamDbException ex) { - logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } catch (TskCoreException ex) { // do nothing. // @@@ Review this behavior - logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS } return new HashMap<>(0); @@ -574,7 +580,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * Get all other abstract files in the current case with the same MD5 as the * selected node. * - * @param corAttr The CorrelationAttribute containing the MD5 to search for + * @param corAttr The CorrelationAttribute containing the MD5 to search for * @param openCase The current case * * @return List of matching AbstractFile objects @@ -708,7 +714,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi correlatedNodeDataMap.values().forEach((nodeData) -> { tableModel.addNodeData(nodeData); - }); } @@ -728,8 +733,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * Adjust a given column for the text provided. * * @param columnIndex The index of the column to adjust. - * @param text The text whose length will be used to adjust the - * column width. + * @param text The text whose length will be used to adjust the column + * width. */ private void setColumnWidthToText(int columnIndex, String text) { TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex); @@ -766,7 +771,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi exportToCSVMenuItem = new javax.swing.JMenuItem(); showCaseDetailsMenuItem = new javax.swing.JMenuItem(); showCommonalityMenuItem = new javax.swing.JMenuItem(); - addCommentMenuItem = new javax.swing.JMenuItem(); CSVFileChooser = new javax.swing.JFileChooser(); otherCasesPanel = new javax.swing.JPanel(); tableContainerPanel = new javax.swing.JPanel(); @@ -798,9 +802,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi org.openide.awt.Mnemonics.setLocalizedText(showCommonalityMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N rightClickPopupMenu.add(showCommonalityMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(addCommentMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.addCommentMenuItem.text")); // NOI18N - rightClickPopupMenu.add(addCommentMenuItem); - setMinimumSize(new java.awt.Dimension(1500, 10)); setOpaque(false); setPreferredSize(new java.awt.Dimension(1500, 44)); @@ -903,15 +904,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi enableCentralRepoActions = instanceData.isCentralRepoNode(); } } - - addCommentMenuItem.setVisible(enableCentralRepoActions); showCaseDetailsMenuItem.setVisible(enableCentralRepoActions); showCommonalityMenuItem.setVisible(enableCentralRepoActions); }//GEN-LAST:event_rightClickPopupMenuPopupMenuWillBecomeVisible // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JFileChooser CSVFileChooser; - private javax.swing.JMenuItem addCommentMenuItem; private javax.swing.JLabel earliestCaseDate; private javax.swing.JLabel earliestCaseLabel; private javax.swing.JMenuItem exportToCSVMenuItem; @@ -993,5 +991,4 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi return dataSourceID; } } - } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 288363fe95..833c27ffcc 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -652,7 +652,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); preparedStatement.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); preparedStatement.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID()); - preparedStatement.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedStatement.setString(4, eamArtifact.getCorrelationValue()); preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); if ("".equals(eamArtifact.getComment())) { @@ -712,10 +712,10 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); + Connection conn = connect(); List artifactInstances = new ArrayList<>(); @@ -743,7 +743,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizedValue); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); @@ -809,8 +809,12 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setString(1, filePath.toLowerCase()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); - artifactInstances.add(artifactInstance); + try { + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); + artifactInstances.add(artifactInstance); + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, "Unable to get artifact instance from resultset.", ex); + } } } catch (SQLException ex) { throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS @@ -834,13 +838,8 @@ abstract class AbstractSqlEamDb implements EamDb { * ArtifactValue. */ @Override - public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } - if (value == null) { - throw new EamDbException("Correlation value is null"); - } + public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); Connection conn = connect(); @@ -856,7 +855,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value.toLowerCase()); + preparedStatement.setString(1, normalizedValue); resultSet = preparedStatement.executeQuery(); resultSet.next(); instanceCount = resultSet.getLong(1); @@ -872,7 +871,7 @@ abstract class AbstractSqlEamDb implements EamDb { } @Override - public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException { + public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException, CorrelationAttributeNormalizationException { if (corAttr == null) { throw new EamDbException("CorrelationAttribute is null"); } @@ -893,10 +892,8 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Number of unique tuples */ @Override - public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); Connection conn = connect(); @@ -914,7 +911,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizedValue); resultSet = preparedStatement.executeQuery(); resultSet.next(); instanceCount = resultSet.getLong(1); @@ -1262,7 +1259,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedQuery.setString(1, eamArtifact.getComment()); preparedQuery.setString(2, eamArtifact.getCorrelationCase().getCaseUUID()); preparedQuery.setString(3, eamArtifact.getCorrelationDataSource().getDeviceID()); - preparedQuery.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedQuery.setString(4, eamArtifact.getCorrelationValue()); preparedQuery.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedQuery.executeUpdate(); } catch (SQLException ex) { @@ -1289,20 +1286,14 @@ abstract class AbstractSqlEamDb implements EamDb { */ @Override public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, - CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException { - - if (type == null) { - throw new EamDbException("Correlation type is null"); - } + CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException { + if (correlationCase == null) { throw new EamDbException("Correlation case is null"); } if (correlationDataSource == null) { throw new EamDbException("Correlation data source is null"); } - if (value == null) { - throw new EamDbException("Correlation value is null"); - } if (filePath == null) { throw new EamDbException("Correlation file path is null"); } @@ -1314,6 +1305,8 @@ abstract class AbstractSqlEamDb implements EamDb { CorrelationAttributeInstance correlationAttributeInstance = null; try { + String normalizedValue = CorrelationAttributeNormalizer.normalize(type, value); + String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); String sql = "SELECT id, known_status, comment FROM " @@ -1326,7 +1319,7 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement = conn.prepareStatement(sql); preparedStatement.setInt(1, correlationCase.getID()); preparedStatement.setInt(2, correlationDataSource.getID()); - preparedStatement.setString(3, value.toLowerCase()); + preparedStatement.setString(3, normalizedValue); preparedStatement.setString(4, filePath.toLowerCase()); resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { @@ -1457,10 +1450,8 @@ abstract class AbstractSqlEamDb implements EamDb { * @return List with 0 or more matching eamArtifact instances. */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); Connection conn = connect(); @@ -1489,7 +1480,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizedValue); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { @@ -1552,8 +1543,12 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setByte(1, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); - artifactInstances.add(artifactInstance); + try { + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet, aType); + artifactInstances.add(artifactInstance); + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, "Unable to get artifact instance from resultset.", ex); + } } } catch (SQLException ex) { throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS @@ -1575,10 +1570,9 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Number of matching eamArtifacts */ @Override - public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); Connection conn = connect(); @@ -1594,7 +1588,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizedValue); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); resultSet.next(); @@ -1623,10 +1617,9 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + + String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); Connection conn = connect(); @@ -1649,7 +1642,7 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizedValue); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { @@ -1768,7 +1761,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException { + public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException, CorrelationAttributeNormalizationException { return isValueInReferenceSet(hash, referenceSetID, CorrelationAttributeInstance.FILES_TYPE_ID); } @@ -1782,8 +1775,10 @@ abstract class AbstractSqlEamDb implements EamDb { * @return true if the value is found in the reference set */ @Override - public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException { + public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException, CorrelationAttributeNormalizationException { + String normalizeValued = CorrelationAttributeNormalizer.normalize(this.getCorrelationTypeById(correlationTypeID), value); + Connection conn = connect(); Long matchingInstances = 0L; @@ -1795,13 +1790,13 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(String.format(sql, fileTableName)); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizeValued); preparedStatement.setInt(2, referenceSetID); resultSet = preparedStatement.executeQuery(); resultSet.next(); matchingInstances = resultSet.getLong(1); } catch (SQLException ex) { - throw new EamDbException("Error determining if value (" + value + ") is in reference set " + referenceSetID, ex); // NON-NLS + throw new EamDbException("Error determining if value (" + normalizeValued + ") is in reference set " + referenceSetID, ex); // NON-NLS } finally { EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); @@ -1820,11 +1815,11 @@ abstract class AbstractSqlEamDb implements EamDb { * @return Global known status of the artifact */ @Override - public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } - + public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { + + //this should be done here so that we can be certain that aType and value are valid before we proceed + String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, value); + // TEMP: Only support file correlation type if (aType.getId() != CorrelationAttributeInstance.FILES_TYPE_ID) { return false; @@ -1837,9 +1832,9 @@ abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String sql = "SELECT count(*) FROM %s WHERE value=? AND known_status=?"; - try { + try { preparedStatement = conn.prepareStatement(String.format(sql, EamDbUtil.correlationTypeToReferenceTableName(aType))); - preparedStatement.setString(1, value); + preparedStatement.setString(1, normalizeValued); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); resultSet.next(); @@ -2420,10 +2415,8 @@ abstract class AbstractSqlEamDb implements EamDb { * @throws EamDbException */ @Override - public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException { - if (aType == null) { - throw new EamDbException("Correlation type is null"); - } + public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException, CorrelationAttributeNormalizationException { + String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, aValue); Connection conn = connect(); @@ -2434,12 +2427,11 @@ abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement1 = conn.prepareStatement(String.format(sql1, EamDbUtil.correlationTypeToReferenceTableName(aType))); - preparedStatement1.setString(1, aValue); + preparedStatement1.setString(1, normalizeValued); resultSet = preparedStatement1.executeQuery(); while (resultSet.next()) { globalFileInstances.add(getEamGlobalFileInstanceFromResultSet(resultSet)); } - return globalFileInstances; } catch (SQLException ex) { throw new EamDbException("Error getting reference instances by type and value.", ex); // NON-NLS @@ -2448,6 +2440,8 @@ abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } + + return globalFileInstances; } /** @@ -2828,7 +2822,7 @@ abstract class AbstractSqlEamDb implements EamDb { * * @throws SQLException when an expected column name is not in the resultSet */ - private CorrelationAttributeInstance getEamArtifactInstanceFromResultSet(ResultSet resultSet, CorrelationAttributeInstance.Type aType) throws SQLException, EamDbException { + private CorrelationAttributeInstance getEamArtifactInstanceFromResultSet(ResultSet resultSet, CorrelationAttributeInstance.Type aType) throws SQLException, EamDbException, CorrelationAttributeNormalizationException { if (null == resultSet) { return null; } @@ -2876,7 +2870,7 @@ abstract class AbstractSqlEamDb implements EamDb { ); } - private EamGlobalFileInstance getEamGlobalFileInstanceFromResultSet(ResultSet resultSet) throws SQLException, EamDbException { + private EamGlobalFileInstance getEamGlobalFileInstanceFromResultSet(ResultSet resultSet) throws SQLException, EamDbException, CorrelationAttributeNormalizationException { if (null == resultSet) { return null; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 9d64f7dc72..261f8c4112 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -55,7 +55,7 @@ public class CorrelationAttributeInstance implements Serializable { CorrelationCase eamCase, CorrelationDataSource eamDataSource, String filePath - ) throws EamDbException { + ) throws EamDbException, CorrelationAttributeNormalizationException { this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN); } @@ -67,7 +67,7 @@ public class CorrelationAttributeInstance implements Serializable { String filePath, String comment, TskData.FileKnown knownStatus - ) throws EamDbException { + ) throws EamDbException, CorrelationAttributeNormalizationException { this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus); } @@ -76,7 +76,7 @@ public class CorrelationAttributeInstance implements Serializable { String correlationValue, CorrelationCase correlationCase, CorrelationDataSource fromTSKDataSource, - String string) throws EamDbException { + String string) throws EamDbException, CorrelationAttributeNormalizationException { this(correlationType, correlationValue, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN); } @@ -86,7 +86,7 @@ public class CorrelationAttributeInstance implements Serializable { * @param aType CorrelationAttributeInstance.Type * @param value correlation value */ - public CorrelationAttributeInstance(Type aType, String value) throws EamDbException { + public CorrelationAttributeInstance(Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { this(aType, value, -1, null, null, "", "", TskData.FileKnown.UNKNOWN); } @@ -99,17 +99,13 @@ public class CorrelationAttributeInstance implements Serializable { String filePath, String comment, TskData.FileKnown knownStatus - ) throws EamDbException { + ) throws EamDbException, CorrelationAttributeNormalizationException { if (filePath == null) { throw new EamDbException("file path is null"); } - if (value == null) { - throw new EamDbException("correlation value is null"); - } - this.correlationType = type; - this.correlationValue = value; + this.correlationValue = CorrelationAttributeNormalizer.normalize(type, value); this.ID = instanceId; this.correlationCase = eamCase; this.correlationDataSource = eamDataSource; @@ -121,6 +117,8 @@ public class CorrelationAttributeInstance implements Serializable { public Boolean equals(CorrelationAttributeInstance otherInstance) { return ((this.getID() == otherInstance.getID()) + && (this.getCorrelationValue().equals(otherInstance.getCorrelationValue())) + && (this.getCorrelationType().equals(otherInstance.getCorrelationType())) && (this.getCorrelationCase().equals(otherInstance.getCorrelationCase())) && (this.getCorrelationDataSource().equals(otherInstance.getCorrelationDataSource())) && (this.getFilePath().equals(otherInstance.getFilePath())) @@ -134,6 +132,8 @@ public class CorrelationAttributeInstance implements Serializable { + this.getCorrelationCase().getCaseUUID() + this.getCorrelationDataSource().getDeviceID() + this.getFilePath() + + this.getCorrelationType().toString() + + this.getCorrelationValue() + this.getKnownStatus() + this.getComment(); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizationException.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizationException.java new file mode 100644 index 0000000000..7bdd56e4a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizationException.java @@ -0,0 +1,53 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.centralrepository.datamodel; + +/** + * Thrown when a given value is not in the expected format. + */ +public class CorrelationAttributeNormalizationException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Construct an exception with the given message. + * @param message error message + */ + public CorrelationAttributeNormalizationException(String message){ + super(message); + } + + /** + * Construct an exception with the given message and inner exception. + * @param message error message + * @param cause inner exception + */ + public CorrelationAttributeNormalizationException(String message, Throwable cause){ + super(message, cause); + } + + /** + * Construct an exception with the given inner exception. + * @param cause inner exception + */ + public CorrelationAttributeNormalizationException(Throwable cause){ + super(cause); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java new file mode 100644 index 0000000000..772e1c517e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java @@ -0,0 +1,160 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.centralrepository.datamodel; + +import java.util.List; +import java.util.Optional; +import org.apache.commons.validator.routines.DomainValidator; +import org.apache.commons.validator.routines.EmailValidator; + +/** + * Provides functions for normalizing data by attribute type before insertion or querying. + */ +final public class CorrelationAttributeNormalizer { + + /** + * This is a utility class - no need for constructing or subclassing, etc... + */ + private CorrelationAttributeNormalizer() { } + + /** + * Normalize the data. Converts text to lower case, and ensures that the + * data is a valid string of the format expected given the attributeType. + * + * @param attributeType correlation type of data + * @param data data to normalize + * + * @return normalized data + */ + public static String normalize(CorrelationAttributeInstance.Type attributeType, String data) throws CorrelationAttributeNormalizationException { + + if(attributeType == null){ + throw new CorrelationAttributeNormalizationException("Attribute type was null."); + } + if(data == null){ + throw new CorrelationAttributeNormalizationException("Data was null."); + } + + switch(attributeType.getId()){ + case CorrelationAttributeInstance.FILES_TYPE_ID: + return normalizeMd5(data); + case CorrelationAttributeInstance.DOMAIN_TYPE_ID: + return normalizeDomain(data); + case CorrelationAttributeInstance.EMAIL_TYPE_ID: + return normalizeEmail(data); + case CorrelationAttributeInstance.PHONE_TYPE_ID: + return normalizePhone(data); + case CorrelationAttributeInstance.USBID_TYPE_ID: + return normalizeUsbId(data); + default: + final String errorMessage = String.format( + "Validator function not found for attribute type: %s", + attributeType.getDisplayName()); + throw new CorrelationAttributeNormalizationException(errorMessage); + } + } + + /** + * Validate the data. Converts text to lower case, and ensures that the + * data is a valid string of the format expected given the attributeType. + * + * @param attributeTypeId correlation type of data + * @param data data to normalize + * + * @return normalized data + */ + public static String normalize(int attributeTypeId, String data) throws CorrelationAttributeNormalizationException { + try { + List defaultTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes(); + Optional typeOption = defaultTypes.stream().filter(attributeType -> attributeType.getId() == attributeTypeId).findAny(); + + if(typeOption.isPresent()){ + CorrelationAttributeInstance.Type type = typeOption.get(); + return CorrelationAttributeNormalizer.normalize(type, data); + } else { + throw new CorrelationAttributeNormalizationException(String.format("Given attributeTypeId did not correspond to any known Attribute: %s", attributeTypeId)); + } + } catch (EamDbException ex) { + throw new CorrelationAttributeNormalizationException(ex); + } + } + + /** + * Verify MD5 is the correct length and values. Make lower case. + */ + private static String normalizeMd5(String data) throws CorrelationAttributeNormalizationException { + final String validMd5Regex = "^[a-f0-9]{32}$"; + final String dataLowered = data.toLowerCase(); + if(dataLowered.matches(validMd5Regex)){ + return dataLowered; + } else { + throw new CorrelationAttributeNormalizationException(String.format("Data purporting to be an MD5 was found not to comform to expected format: %s", data)); + } + } + + /** + * Verify there are no slashes or invalid domain name characters (such as '?' or \: ). Normalize to lower case. + */ + private static String normalizeDomain(String data) throws CorrelationAttributeNormalizationException { + DomainValidator validator = DomainValidator.getInstance(true); + if(validator.isValid(data)){ + return data.toLowerCase(); + } else { + final String validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"; + if(data.matches(validIpAddressRegex)){ + return data; + } else { + throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid domain: %s", data)); + } + } + } + + /** + * Verify that there is an '@' and no invalid characters. Should normalize to lower case. + */ + private static String normalizeEmail(String data) throws CorrelationAttributeNormalizationException { + EmailValidator validator = EmailValidator.getInstance(true, true); + if(validator.isValid(data)){ + return data.toLowerCase(); + } else { + throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid email address: %s", data)); + } + } + + /** + * Verify it is only numbers and '+'. Strip spaces, dashes, and parentheses. + */ + private static String normalizePhone(String data) throws CorrelationAttributeNormalizationException { + if(data.matches("\\+?[0-9()\\-\\s]+")){ + String phoneNumber = data.replaceAll("[^0-9\\+]", ""); + return phoneNumber; + } else { + throw new CorrelationAttributeNormalizationException(String.format("Data was expected to be a valid phone number: %s", data)); + } + } + + /** + * Vacuous - will be replaced with something reasonable later. + */ + private static String normalizeUsbId(String data) throws CorrelationAttributeNormalizationException { + //TODO replace with correct usb id validation at a later date + return data; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 48aea257ba..095c1a7139 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -213,7 +213,7 @@ public class EamArtifactUtil { TskData.FileKnown.UNKNOWN ); - } catch (TskCoreException | EamDbException ex) { + } catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) { logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS return null; } catch (NoCurrentCaseException ex) { @@ -251,7 +251,8 @@ public class EamArtifactUtil { type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); if (null == correlationCase) { - correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); + //if the correlationCase is not in the Central repo then attributes generated in relation to it will not be + return null; } correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()); value = file.getMd5Hash(); @@ -267,7 +268,7 @@ public class EamArtifactUtil { CorrelationAttributeInstance correlationAttributeInstance; try { correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, String.format( "Correlation attribute could not be retrieved for '%s' (id=%d): %s", content.getName(), content.getId(), ex.getMessage())); @@ -322,7 +323,7 @@ public class EamArtifactUtil { CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()), af.getParentPath() + af.getName()); - } catch (TskCoreException | EamDbException ex) { + } catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) { logger.log(Level.SEVERE, "Error making correlation attribute.", ex); return null; } catch (NoCurrentCaseException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index a58daa4b9b..d7b725109b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -244,7 +244,7 @@ public interface EamDb { * * @return List of artifact instances for a given type/value */ - List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Retrieves eamArtifact instances from the database that are associated @@ -269,7 +269,7 @@ public interface EamDb { * @return Number of artifact instances having ArtifactType and * ArtifactValue. */ - Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Calculate the percentage of data sources that have this attribute value. @@ -278,7 +278,7 @@ public interface EamDb { * * @return Int between 0 and 100 */ - int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException; + int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException, CorrelationAttributeNormalizationException; /** * Retrieves number of unique caseDisplayName / dataSource tuples in the @@ -290,7 +290,7 @@ public interface EamDb { * * @return Number of unique tuples */ - Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Retrieves number of data sources in the database. @@ -358,7 +358,7 @@ public interface EamDb { * @throws EamDbException */ CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, - CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException; + CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException; /** * Sets an eamArtifact instance to the given known status. If eamArtifact @@ -378,7 +378,7 @@ public interface EamDb { * * @return List with 0 or more matching eamArtifact instances. */ - List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Gets list of matching eamArtifact instances that have knownStatus = @@ -397,7 +397,7 @@ public interface EamDb { * * @return Number of matching eamArtifacts */ - Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Gets list of distinct case display names, where each case has 1+ Artifact @@ -411,7 +411,7 @@ public interface EamDb { * * @throws EamDbException */ - List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Remove a reference set and all values contained in it. @@ -462,7 +462,7 @@ public interface EamDb { * * @throws EamDbException */ - public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException; + public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException, CorrelationAttributeNormalizationException; /** * Check if the given value is in a specific reference set @@ -473,7 +473,7 @@ public interface EamDb { * * @return true if the hash is found in the reference set */ - public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException; + public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException, CorrelationAttributeNormalizationException; /** * Is the artifact known as bad according to the reference entries? @@ -483,7 +483,7 @@ public interface EamDb { * * @return Global known status of the artifact */ - boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException; + boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; /** * Add a new organization @@ -611,7 +611,7 @@ public interface EamDb { * * @throws EamDbException */ - List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException; + List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException, CorrelationAttributeNormalizationException; /** * Add a new EamArtifact.Type to the db. diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java index 3c538e67c8..9f51752251 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java @@ -36,7 +36,7 @@ public class EamGlobalFileInstance { int globalSetID, String MD5Hash, TskData.FileKnown knownStatus, - String comment) throws EamDbException { + String comment) throws EamDbException, CorrelationAttributeNormalizationException { this(-1, globalSetID, MD5Hash, knownStatus, comment); } @@ -45,17 +45,14 @@ public class EamGlobalFileInstance { int globalSetID, String MD5Hash, TskData.FileKnown knownStatus, - String comment) throws EamDbException { - if(MD5Hash == null){ - throw new EamDbException("null MD5 hash"); - } + String comment) throws EamDbException, CorrelationAttributeNormalizationException { + if(knownStatus == null){ throw new EamDbException("null known status"); } this.instanceID = instanceID; this.globalSetID = globalSetID; - // Normalize hashes by lower casing - this.MD5Hash = MD5Hash.toLowerCase(); + this.MD5Hash = CorrelationAttributeNormalizer.normalize(CorrelationAttributeInstance.FILES_TYPE_ID, MD5Hash); this.knownStatus = knownStatus; this.comment = comment; } @@ -117,12 +114,8 @@ public class EamGlobalFileInstance { /** * @param MD5Hash the MD5Hash to set */ - public void setMD5Hash(String MD5Hash) throws EamDbException { - if(MD5Hash == null){ - throw new EamDbException("null MD5 hash"); - } - // Normalize hashes by lower casing - this.MD5Hash = MD5Hash.toLowerCase(); + public void setMD5Hash(String MD5Hash) throws CorrelationAttributeNormalizationException { + this.MD5Hash = CorrelationAttributeNormalizer.normalize(CorrelationAttributeInstance.FILES_TYPE_ID, MD5Hash); } /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java index d7da43bbf4..6816bebb32 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java @@ -106,4 +106,5 @@ public interface InstanceTableCallback { return resultSet.getString("comment"); } + } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index d300964b5f..4805e4e0b0 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -447,7 +447,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return List of artifact instances for a given type/value */ @Override - public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public List getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getArtifactInstancesByTypeValue(aType, value); @@ -489,7 +489,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getCountArtifactInstancesByTypeValue(aType, value); @@ -499,7 +499,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { } @Override - public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException { + public int getFrequencyPercentage(CorrelationAttributeInstance corAttr) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getFrequencyPercentage(corAttr); @@ -520,7 +520,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value); @@ -617,7 +617,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return List with 0 or more matching eamArtifact instances. */ @Override - public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public List getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getArtifactInstancesKnownBad(aType, value); @@ -654,7 +654,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return Number of matching eamArtifacts */ @Override - public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getCountArtifactInstancesKnownBad(aType, value); @@ -676,7 +676,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getListCasesHavingArtifactInstancesKnownBad(aType, value); @@ -710,7 +710,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return true if the hash is found in the reference set */ @Override - public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException { + public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.isValueInReferenceSet(value, referenceSetID, correlationTypeID); @@ -782,7 +782,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return Global known status of the artifact */ @Override - public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { + public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.isArtifactKnownBadByReference(aType, value); @@ -967,7 +967,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException { + public List getReferenceInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String aValue) throws EamDbException, CorrelationAttributeNormalizationException { try { acquireSharedLock(); return super.getReferenceInstancesByTypeValue(aType, aValue); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index e432c4bf6b..ccca659574 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -34,6 +34,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -123,19 +124,19 @@ public class IngestEventsListener { public synchronized static int getCeModuleInstanceCount() { return correlationModuleInstanceCount; } - + /** * Are notable items being flagged? - * + * * @return True if flagging notable items; otherwise false. */ public synchronized static boolean isFlagNotableItems() { return flagNotableItems; } - + /** * Configure the listener to flag notable items or not. - * + * * @param value True to flag notable items; otherwise false. */ public synchronized static void setFlagNotableItems(boolean value) { @@ -259,13 +260,18 @@ public class IngestEventsListener { if (recentlyAddedCeArtifacts.add(eamArtifact.toString())) { // Was it previously marked as bad? // query db for artifact instances having this TYPE/VALUE and knownStatus = "Bad". - // if gettKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case, + // if getKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case, // create TSK_INTERESTING_ARTIFACT_HIT artifact on BB. if (flagNotableItemsEnabled) { - List caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); - if (!caseDisplayNames.isEmpty()) { - postCorrelatedBadArtifactToBlackboard(bbArtifact, - caseDisplayNames); + List caseDisplayNames; + try { + caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); + if (!caseDisplayNames.isEmpty()) { + postCorrelatedBadArtifactToBlackboard(bbArtifact, + caseDisplayNames); + } + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex); } } eamArtifacts.add(eamArtifact); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 441380bcc6..21baf59454 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.FileIngestModule; @@ -143,6 +144,9 @@ final class IngestModule implements FileIngestModule { } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS return ProcessResult.ERROR; + } catch (CorrelationAttributeNormalizationException ex){ + logger.log(Level.INFO, "Error searching database for artifact.", ex); // NON-NLS + return ProcessResult.ERROR; } } @@ -161,6 +165,9 @@ final class IngestModule implements FileIngestModule { } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS return ProcessResult.ERROR; + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.INFO, "Error adding artifact to bulk artifacts.", ex); // NON-NLS + return ProcessResult.ERROR; } return ProcessResult.OK; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java index c8ecb3225b..7f4d6605ab 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java @@ -71,6 +71,12 @@ public abstract class AbstractCommonAttributeInstance { this.caseName = ""; this.dataSource = ""; } + + /** + * Get the type of common attribute. + * @return + */ + public abstract CorrelationAttributeInstance.Type getCorrelationAttributeInstanceType(); /** * Get an AbstractFile for this instance if it can be retrieved from the diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java index 62cbbeb24b..9804d06ca9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -33,9 +33,10 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.TskCoreException; + /** * Prototype for an object which finds files with common attributes. - * Subclass this and implement findFiles in order + * Subclass this and implement findMatches in order */ public abstract class AbstractCommonAttributeSearcher { @@ -67,7 +68,7 @@ public abstract class AbstractCommonAttributeSearcher { * @throws SQLException * @throws EamDbException */ - public abstract CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; + public abstract CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; /** * Implement this to create a descriptive string for the tab which will display @@ -75,10 +76,10 @@ public abstract class AbstractCommonAttributeSearcher { * @return an informative string */ @NbBundle.Messages({ - "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraAll=Common Files (All Data Sources, %s)", - "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraSingle=Common Files (Data Source: %s, %s)", - "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterAll=Common Files (All Central Repository Cases, %s)", - "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterSingle=Common Files (Central Repository Case: %s, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraAll=Common Attributes (All Data Sources, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraSingle=Common Attributes (Data Source: %s, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterAll=Common Attributes (All Central Repository Cases, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterSingle=Common Attributes (Central Repository Case: %s, %s)", }) abstract String buildTabTitle(); @@ -87,6 +88,7 @@ public abstract class AbstractCommonAttributeSearcher { "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.media=Media", "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories" }) + String buildCategorySelectionString() { if (!this.isFilterByDoc() && !this.isFilterByMedia()) { return Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_all(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java index ac84d0188f..d8a7f27b44 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -20,12 +20,12 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.sql.SQLException; -import java.util.List; import java.util.Map; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; /** * Algorithm which finds files anywhere in the Central Repo which also occur in @@ -34,28 +34,27 @@ import org.sleuthkit.datamodel.TskCoreException; public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher { /** - * + * * @param filterByMediaMimeType match only on files whose mime types can be * broadly categorized as media types * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types - * @throws EamDbException + * @throws EamDbException */ - public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); + public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold); } @Override - public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); + public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), corAttrType); Map interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); - return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType); } @Override String buildTabTitle() { - final String buildCategorySelectionString = this.buildCategorySelectionString(); final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterAll(); - return String.format(titleTemplate, new Object[]{buildCategorySelectionString}); + return String.format(titleTemplate, new Object[]{this.corAttrType.getDisplayName()}); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index b8f2e3e0d7..fb0e22ba54 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -27,4 +27,11 @@ CommonAttributePanel.allFileCategoriesRadioButton.text=All file types CommonAttributePanel.cancelButton.actionCommand=Cancel CommonAttributePanel.cancelButton.text=Cancel CommonAttributePanel.searchButton.text=Search +CommonAttributePanel.jCheckBox1.text=Hide files found in over +CommonAttributePanel.jLabel1.text=% of data sources in central repository. +CommonAttributePanel.percentageThreshold.text=20 +CommonAttributePanel.jLabel1.text_1=% of data sources in central repository. +CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over +InterCasePanel.comboBoxLabel.text=Select correlation type to search: +InterCasePanel.correlationTypeComboBox.toolTipText=Selected Correlation Type CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java index 9221a5dc86..7bf6801c6b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.datamodel.AbstractFile; @@ -70,4 +71,10 @@ final public class CaseDBCommonAttributeInstance extends AbstractCommonAttribute return null; } } + + @Override + public CorrelationAttributeInstance.Type getCorrelationAttributeInstanceType() { + //may be required at a later date + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java index f105d72498..45ba05735e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java @@ -18,28 +18,33 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.ContentTag; /** - * Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored - * in the CaseDB. + * Node that wraps CaseDBCommonAttributeInstance to represent a file instance + * stored in the CaseDB. */ public class CaseDBCommonAttributeInstanceNode extends FileNode { - + private final String caseName; private final String dataSource; /** * Create a node which can be used in a multilayer tree table and is based * on an AbstractFile. + * + * @param fsContent the file which is being represented by this node + * @param caseName the name of the case + * @param dataSource the datasource which contains the file * - * @param fsContent - * @param dataSource */ public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) { super(fsContent); @@ -48,20 +53,20 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode { } @Override - public boolean isLeafTypeNode(){ + public boolean isLeafTypeNode() { //Not used atm - could maybe be leveraged for better use in Children objects return true; } - + @Override public T accept(DisplayableItemNodeVisitor visitor) { return visitor.visit(this); } - public String getCase(){ + public String getCase() { return this.caseName; } - + public String getDataSource() { return this.dataSource; } @@ -74,18 +79,22 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } + List tags = getContentTagsFromDatabase(); final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); - + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName())); + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + this.addScoreProperty(sheetSet, tags); + this.addCommentProperty(sheetSet, tags, correlationAttribute); + this.addCountProperty(sheetSet, correlationAttribute); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent()))); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType()))); + this.addTagProperty(sheetSet, tags); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); - - this.addTagProperty(sheetSet); - return sheet; } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java index 6cb58b9643..df6d24b0bb 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java @@ -44,12 +44,19 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName()); private final Integer crFileId; private CorrelationAttributeInstance currentAttribute; + private final CorrelationAttributeInstance.Type correlationType; private final Map dataSourceNameToIdMap; - CentralRepoCommonAttributeInstance(Integer attrInstId, Map dataSourceIdToNameMap) { + CentralRepoCommonAttributeInstance(Integer attrInstId, Map dataSourceIdToNameMap, CorrelationAttributeInstance.Type correlationType) { super(); this.crFileId = attrInstId; this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap); + this.correlationType = correlationType; + } + + @Override + public CorrelationAttributeInstance.Type getCorrelationAttributeInstanceType(){ + return this.correlationType; } void setCurrentAttributeInst(CorrelationAttributeInstance attribute) { @@ -61,16 +68,15 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr Case currentCase; if (this.currentAttribute != null) { - + final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute; - + String currentFullPath = currentAttributeInstance.getFilePath(); String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName(); - - - if(this.dataSourceNameToIdMap.containsKey(currentDataSource)){ + + if (this.dataSourceNameToIdMap.containsKey(currentDataSource)) { Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource); - + try { currentCase = Case.getCurrentCaseThrows(); @@ -83,9 +89,9 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSourceObjectId); List potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause); - if(potentialAbstractFiles.isEmpty()){ + if (potentialAbstractFiles.isEmpty()) { return null; - } else if(potentialAbstractFiles.size() > 1){ + } else if (potentialAbstractFiles.size() > 1) { LOGGER.log(Level.WARNING, String.format("Unable to find an exact match for AbstractFile for record with filePath: %s. May have returned the wrong file.", new Object[]{currentFullPath})); return potentialAbstractFiles.get(0); } else { @@ -98,7 +104,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr } } else { return null; - } + } } return null; } @@ -107,7 +113,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr public DisplayableItemNode[] generateNodes() { // @@@ We should be doing more of this work in teh generateKeys method. We want to do as little as possible in generateNodes - InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(); + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(correlationType); CorrelationAttributeInstance corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId); List attrInstNodeList = new ArrayList<>(0); String currCaseDbName = Case.getCurrentCase().getDisplayName(); @@ -128,7 +134,7 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr private Map invertMap(Map dataSourceIdToNameMap) { HashMap invertedMap = new HashMap<>(); - for (Map.Entry entry : dataSourceIdToNameMap.entrySet()){ + for (Map.Entry entry : dataSourceIdToNameMap.entrySet()) { invertedMap.put(entry.getValue(), entry.getKey()); } return invertedMap; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 20cbb33fe9..ff9799dc7d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -9,10 +9,10 @@ - + - + @@ -32,7 +32,7 @@ - + @@ -40,13 +40,13 @@ - + - + - + @@ -107,7 +107,7 @@ - + @@ -199,6 +199,7 @@ + @@ -215,7 +216,6 @@ - @@ -233,6 +233,7 @@ + @@ -244,6 +245,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 21f2b72624..c4027d74cf 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -38,6 +38,7 @@ import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -74,25 +75,26 @@ public final class CommonAttributePanel extends javax.swing.JDialog { * Creates new form CommonFilesPanel */ @NbBundle.Messages({ - "CommonFilesPanel.title=Common Files Panel", - "CommonFilesPanel.exception=Unexpected Exception loading DataSources.", - "CommonFilesPanel.frame.title=Find Common Files", - "CommonFilesPanel.frame.msg=Find Common Files"}) + "CommonAttributePanel.title=Common Attribute Panel", + "CommonAttributePanel.exception=Unexpected Exception loading DataSources.", + "CommonAttributePanel.frame.title=Find Common Attributes", + "CommonAttributePanel.frame.msg=Find Common Attributes"}) public CommonAttributePanel() { - super(new JFrame(Bundle.CommonFilesPanel_frame_title()), - Bundle.CommonFilesPanel_frame_msg(), true); + super(new JFrame(Bundle.CommonAttributePanel_frame_title()), + Bundle.CommonAttributePanel_frame_msg(), true); initComponents(); - + this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { this.setupCases(); + this.interCasePanel.setupCorrelationTypeFilter(); } else { this.disableIntercaseSearch(); } - - if(CommonAttributePanel.isEamDbAvailableForPercentageFrequencyCalculations()){ + + if (CommonAttributePanel.isEamDbAvailableForPercentageFrequencyCalculations()) { this.enablePercentageOptions(); } else { this.disablePercentageOptions(); @@ -103,11 +105,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){ private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize(); - - private void maintainSize(){ + + private void maintainSize() { CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize); } - + @Override public void insertUpdate(DocumentEvent event) { this.maintainSize(); @@ -141,9 +143,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } return false; } - - private static boolean isEamDbAvailableForPercentageFrequencyCalculations(){ - try { + + private static boolean isEamDbAvailableForPercentageFrequencyCalculations() { + try { return EamDb.isEnabled() && EamDb.getInstance() != null && EamDb.getInstance().getCases().size() > 0; @@ -159,18 +161,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } @NbBundle.Messages({ - "CommonFilesPanel.search.results.titleAll=Common Files (All Data Sources)", - "CommonFilesPanel.search.results.titleSingle=Common Files (Match Within Data Source: %s)", - "CommonFilesPanel.search.results.pathText=Common Files Search Results", - "CommonFilesPanel.search.done.searchProgressGathering=Gathering Common Files Search Results.", - "CommonFilesPanel.search.done.searchProgressDisplay=Displaying Common Files Search Results.", - "CommonFilesPanel.search.done.tskCoreException=Unable to run query against DB.", - "CommonFilesPanel.search.done.noCurrentCaseException=Unable to open case file.", - "CommonFilesPanel.search.done.exception=Unexpected exception running Common Files Search.", - "CommonFilesPanel.search.done.interupted=Something went wrong finding common files.", - "CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."}) + "CommonAttributePanel.search.results.titleAll=Common Attributes (All Data Sources)", + "CommonAttributePanel.search.results.titleSingle=Common Attributes (Match Within Data Source: %s)", + "CommonAttributePanel.search.results.pathText=Common Attribute Search Results", + "CommonAttributePanel.search.done.searchProgressGathering=Gathering Common Attribute Search Results.", + "CommonAttributePanel.search.done.searchProgressDisplay=Displaying Common Attribute Search Results.", + "CommonAttributePanel.search.done.tskCoreException=Unable to run query against DB.", + "CommonAttributePanel.search.done.noCurrentCaseException=Unable to open case file.", + "CommonAttributePanel.search.done.exception=Unexpected exception running Common Attribute Search.", + "CommonAttributePanel.search.done.interupted=Something went wrong finding common attributes.", + "CommonAttributePanel.search.done.sqlException=Unable to query db for attributes or data sources."}) private void search() { - String pathText = Bundle.CommonFilesPanel_search_results_pathText(); + String pathText = Bundle.CommonAttributePanel_search_results_pathText(); new SwingWorker() { @@ -178,20 +180,20 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private ProgressHandle progress; private void setTitleForAllDataSources() { - this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll(); + this.tabTitle = Bundle.CommonAttributePanel_search_results_titleAll(); } private void setTitleForSingleSource(Long dataSourceId) { - final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle(); + final String CommonAttributePanel_search_results_titleSingle = Bundle.CommonAttributePanel_search_results_titleSingle(); final Object[] dataSourceName = new Object[]{intraCasePanel.getDataSourceMap().get(dataSourceId)}; - this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName); + this.tabTitle = String.format(CommonAttributePanel_search_results_titleSingle, dataSourceName); } @Override @SuppressWarnings({"BoxedValueEquality", "NumberEquality"}) protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering()); + progress = ProgressHandle.createHandle(Bundle.CommonAttributePanel_search_done_searchProgressGathering()); progress.start(); progress.switchToIndeterminate(); @@ -213,18 +215,22 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue; - + if (!CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { //0 has the effect of disabling the feature percentageThreshold = 0; } if (CommonAttributePanel.this.interCaseRadio.isSelected()) { - + CorrelationAttributeInstance.Type corType = interCasePanel.getSelectedCorrelationType(); + if (corType == null) { + corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); + } if (caseId == InterCasePanel.NO_CASE_SELECTED) { - builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); + builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, corType, percentageThreshold); } else { - builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); + + builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, corType, percentageThreshold); } } else { if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) { @@ -237,7 +243,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { setTitleForSingleSource(dataSourceId); } } - metadata = builder.findFiles(); + metadata = builder.findMatches(); this.tabTitle = builder.buildTabTitle(); return metadata; @@ -262,28 +268,28 @@ public final class CommonAttributePanel extends javax.swing.JDialog { Collection viewers = new ArrayList<>(1); viewers.add(table); - progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay()); + progress.setDisplayName(Bundle.CommonAttributePanel_search_done_searchProgressDisplay()); DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); progress.finish(); } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted()); + MessageNotifyUtil.Message.error(Bundle.CommonAttributePanel_search_done_interupted()); } catch (ExecutionException ex) { String errorMessage; Throwable inner = ex.getCause(); if (inner instanceof TskCoreException) { LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException(); + errorMessage = Bundle.CommonAttributePanel_search_done_tskCoreException(); } else if (inner instanceof NoCurrentCaseException) { LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException(); + errorMessage = Bundle.CommonAttributePanel_search_done_noCurrentCaseException(); } else if (inner instanceof SQLException) { LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_sqlException(); + errorMessage = Bundle.CommonAttributePanel_search_done_sqlException(); } else { LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_exception(); + errorMessage = Bundle.CommonAttributePanel_search_done_exception(); } MessageNotifyUtil.Message.error(errorMessage); } @@ -299,12 +305,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { * names */ @NbBundle.Messages({ - "CommonFilesPanel.setupDataSources.done.tskCoreException=Unable to run query against DB.", - "CommonFilesPanel.setupDataSources.done.noCurrentCaseException=Unable to open case file.", - "CommonFilesPanel.setupDataSources.done.exception=Unexpected exception loading data sources.", - "CommonFilesPanel.setupDataSources.done.interupted=Something went wrong building the Common Files Search dialog box.", - "CommonFilesPanel.setupDataSources.done.sqlException=Unable to query db for data sources.", - "CommonFilesPanel.setupDataSources.updateUi.noDataSources=No data sources were found."}) + "CommonAttributePanel.setupDataSources.done.tskCoreException=Unable to run query against DB.", + "CommonAttributePanel.setupDataSources.done.noCurrentCaseException=Unable to open case file.", + "CommonAttributePanel.setupDataSources.done.exception=Unexpected exception loading data sources.", + "CommonAttributePanel.setupDataSources.done.interupted=Something went wrong building the Common Files Search dialog box.", + "CommonAttributePanel.setupDataSources.done.sqlException=Unable to query db for data sources.", + "CommonAttributePanel.setupDataSources.updateUi.noDataSources=No data sources were found."}) private void setupDataSources() { new SwingWorker, Void>() { @@ -346,22 +352,22 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupDataSources_done_interupted()); + MessageNotifyUtil.Message.error(Bundle.CommonAttributePanel_setupDataSources_done_interupted()); } catch (ExecutionException ex) { String errorMessage; Throwable inner = ex.getCause(); if (inner instanceof TskCoreException) { LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex); - errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_tskCoreException(); + errorMessage = Bundle.CommonAttributePanel_setupDataSources_done_tskCoreException(); } else if (inner instanceof NoCurrentCaseException) { LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); - errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_noCurrentCaseException(); + errorMessage = Bundle.CommonAttributePanel_setupDataSources_done_noCurrentCaseException(); } else if (inner instanceof SQLException) { LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex); - errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_sqlException(); + errorMessage = Bundle.CommonAttributePanel_setupDataSources_done_sqlException(); } else { LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex); - errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_exception(); + errorMessage = Bundle.CommonAttributePanel_setupDataSources_done_exception(); } MessageNotifyUtil.Message.error(errorMessage); } @@ -370,8 +376,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } @NbBundle.Messages({ - "CommonFilesPanel.setupCases.done.interruptedException=Something went wrong building the Common Files Search dialog box.", - "CommonFilesPanel.setupCases.done.exeutionException=Unexpected exception loading cases."}) + "CommonAttributePanel.setupCases.done.interruptedException=Something went wrong building the Common Files Search dialog box.", + "CommonAttributePanel.setupCases.done.exeutionException=Unexpected exception loading cases."}) private void setupCases() { new SwingWorker, Void>() { @@ -423,10 +429,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.updateUi(); } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_interruptedException()); + MessageNotifyUtil.Message.error(Bundle.CommonAttributePanel_setupCases_done_interruptedException()); } catch (ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog.", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_exeutionException()); + MessageNotifyUtil.Message.error(Bundle.CommonAttributePanel_setupCases_done_exeutionException()); } } @@ -470,8 +476,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); - setMaximumSize(new java.awt.Dimension(450, 375)); - setMinimumSize(new java.awt.Dimension(450, 375)); + setMaximumSize(new java.awt.Dimension(450, 440)); + setMinimumSize(new java.awt.Dimension(450, 440)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -479,9 +485,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setMaximumSize(new java.awt.Dimension(450, 375)); - jPanel1.setMinimumSize(new java.awt.Dimension(450, 375)); - jPanel1.setPreferredSize(new java.awt.Dimension(450, 375)); + jPanel1.setMaximumSize(new java.awt.Dimension(450, 440)); + jPanel1.setMinimumSize(new java.awt.Dimension(450, 440)); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 440)); jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N @@ -506,6 +512,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton); + allFileCategoriesRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.text")); // NOI18N allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() { @@ -515,7 +522,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); fileTypeFilterButtonGroup.add(selectedFileCategoriesButton); - selectedFileCategoriesButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.text")); // NOI18N selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() { @@ -526,6 +532,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { pictureVideoCheckbox.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.pictureVideoCheckbox.text")); // NOI18N + pictureVideoCheckbox.setEnabled(false); pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { pictureVideoCheckboxActionPerformed(evt); @@ -534,6 +541,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { documentsCheckbox.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.documentsCheckbox.text")); // NOI18N + documentsCheckbox.setEnabled(false); documentsCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { documentsCheckboxActionPerformed(evt); @@ -628,7 +636,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(percentageThresholdTextTwo))) - .addContainerGap(9, Short.MAX_VALUE)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -689,10 +697,19 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + this.categoriesLabel.setEnabled(false); + this.selectedFileCategoriesButton.setEnabled(false); + this.allFileCategoriesRadioButton.setEnabled(false); + this.allFileCategoriesRadioButton.setSelected(true); + this.documentsCheckbox.setEnabled(false); + this.pictureVideoCheckbox.setEnabled(false); }//GEN-LAST:event_interCaseRadioActionPerformed private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + this.categoriesLabel.setEnabled(true); + this.selectedFileCategoriesButton.setEnabled(true); + this.allFileCategoriesRadioButton.setEnabled(true); }//GEN-LAST:event_intraCaseRadioActionPerformed private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed @@ -720,19 +737,19 @@ public final class CommonAttributePanel extends javax.swing.JDialog { SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_searchButtonActionPerformed - private void percentageThresholdChanged(){ + private void percentageThresholdChanged() { String percentageString = this.percentageThresholdTextOne.getText(); try { this.percentageThresholdValue = Integer.parseInt(percentageString); - + } catch (NumberFormatException exception) { this.percentageThresholdValue = -1; } - this.handleFrequencyPercentageState(); + this.handleFrequencyPercentageState(); } - + private void updateErrorTextAndSearchBox() { if (this.errorManager.anyErrors()) { @@ -768,7 +785,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { if (this.allFileCategoriesRadioButton.isSelected()) { this.pictureVideoCheckbox.setEnabled(false); this.documentsCheckbox.setEnabled(false); - + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); } @@ -786,7 +803,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); } } - + this.updateErrorTextAndSearchBox(); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchAction.java similarity index 86% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchAction.java index 6cc00256dd..61dfd8d578 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchAction.java @@ -32,14 +32,14 @@ import org.sleuthkit.autopsy.coreutils.Logger; /** * Encapsulates a menu action which triggers the common files search dialog. */ -final public class CommonFilesSearchAction extends CallableSystemAction { +final public class CommonAttributeSearchAction extends CallableSystemAction { - private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchAction.class.getName()); + private static final Logger LOGGER = Logger.getLogger(CommonAttributeSearchAction.class.getName()); - private static CommonFilesSearchAction instance = null; + private static CommonAttributeSearchAction instance = null; private static final long serialVersionUID = 1L; - CommonFilesSearchAction() { + CommonAttributeSearchAction() { super(); this.setEnabled(false); } @@ -68,9 +68,9 @@ final public class CommonFilesSearchAction extends CallableSystemAction { return super.isEnabled() && shouldBeEnabled; } - public static synchronized CommonFilesSearchAction getDefault() { + public static synchronized CommonAttributeSearchAction getDefault() { if (instance == null) { - instance = new CommonFilesSearchAction(); + instance = new CommonAttributeSearchAction(); } return instance; } @@ -86,10 +86,10 @@ final public class CommonFilesSearchAction extends CallableSystemAction { } @NbBundle.Messages({ - "CommonFilesAction.getName.text=Common Files Search"}) + "CommonAttributeSearchAction.getName.text=Common Attribute Search"}) @Override public String getName() { - return Bundle.CommonFilesAction_getName_text(); + return Bundle.CommonAttributeSearchAction_getName_text(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index bac59fde12..036b97a92a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -25,9 +25,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; /** * Stores the results from the various types of common attribute searching @@ -35,10 +38,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; */ final public class CommonAttributeSearchResults { + private static final Logger LOGGER = Logger.getLogger(CommonAttributeSearchResults.class.getName()); + // maps instance count to list of attribute values. private final Map instanceCountToAttributeValues; private final int percentageThreshold; + private final int resultTypeId; /** * Create a values object which can be handed off to the node factories. @@ -46,10 +52,24 @@ final public class CommonAttributeSearchResults { * @param values list of CommonAttributeValue indexed by size of * CommonAttributeValue */ + CommonAttributeSearchResults(Map metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType) { + //wrap in a new object in case any client code has used an unmodifiable collection + this.instanceCountToAttributeValues = new HashMap<>(metadata); + this.percentageThreshold = percentageThreshold; + this.resultTypeId = resultType.getId(); + } + + /** + * Create a values object which can be handed off to the node factories. + * + * @param values list of CommonAttributeValue indexed by size of + * CommonAttributeValue + */ CommonAttributeSearchResults(Map metadata, int percentageThreshold) { //wrap in a new object in case any client code has used an unmodifiable collection this.instanceCountToAttributeValues = new HashMap<>(metadata); this.percentageThreshold = percentageThreshold; + this.resultTypeId = CorrelationAttributeInstance.FILES_TYPE_ID; } /** @@ -86,7 +106,7 @@ final public class CommonAttributeSearchResults { * search. * * Remove results which are not found in the portion of available data - sources described by maximumPercentageThreshold. + * sources described by maximumPercentageThreshold. * * @return metadata */ @@ -99,7 +119,7 @@ final public class CommonAttributeSearchResults { CorrelationAttributeInstance.Type fileAttributeType = CorrelationAttributeInstance .getDefaultCorrelationTypes() .stream() - .filter(filterType -> filterType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) + .filter(filterType -> filterType.getId() == this.resultTypeId) .findFirst().get(); EamDb eamDb = EamDb.getInstance(); @@ -113,16 +133,20 @@ final public class CommonAttributeSearchResults { for(CommonAttributeValue value : values.getDelayedMetadataList()){ // Need the real metadata - int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); + try { + int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); - if(frequencyPercentage > maximumPercentageThreshold){ - if(itemsToRemove.containsKey(key)){ - itemsToRemove.get(key).add(value); - } else { - List toRemove = new ArrayList<>(); - toRemove.add(value); - itemsToRemove.put(key, toRemove); + if(frequencyPercentage > maximumPercentageThreshold){ + if(itemsToRemove.containsKey(key)){ + itemsToRemove.get(key).add(value); + } else { + List toRemove = new ArrayList<>(); + toRemove.add(value); + itemsToRemove.put(key, toRemove); + } } + } catch(CorrelationAttributeNormalizationException ex){ + LOGGER.log(Level.WARNING, "Unable to determine frequency percentage attribute - frequency filter may not be accurate for these results.", ex); } } } @@ -154,7 +178,7 @@ final public class CommonAttributeSearchResults { int count = 0; for (CommonAttributeValueList data : this.instanceCountToAttributeValues.values()) { - for(CommonAttributeValue md5 : data.getMetadataList()){ + for(CommonAttributeValue md5 : data.getDelayedMetadataList()){ count += md5.getInstanceCount(); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java index f6372c89f7..967a205a73 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java @@ -86,7 +86,4 @@ final public class CommonAttributeValue { public int getInstanceCount() { return this.fileInstances.size(); } - - - } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java index c7c5dea58b..219072fa2a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java @@ -41,7 +41,7 @@ public class CommonAttributeValueNode extends DisplayableItemNode { private final String dataSources; @NbBundle.Messages({ - "Md5Node.Md5Node.format=MD5: %s" + "CommonAttributeValueNode.CommonAttributeValueNode.format=Value: %s" }) /** * Create a Match node whose children will all have this object in common. @@ -57,7 +57,7 @@ public class CommonAttributeValueNode extends DisplayableItemNode { this.dataSources = String.join(", ", data.getDataSources()); this.value = data.getValue(); - this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.value)); + this.setDisplayName(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value)); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java index d1bff54c99..931ee7ffcc 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java @@ -23,6 +23,7 @@ import java.util.Map; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; /** * Provides logic for selecting common files from all data sources and all cases @@ -31,6 +32,10 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher { private final EamDb dbManager; + /** + * The Correlation Type to find matches on. + */ + final Type corAttrType; /** * Implements the algorithm for getting common files across all data sources @@ -43,9 +48,10 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS * * @throws EamDbException */ - InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); dbManager = EamDb.getInstance(); + this.corAttrType = corAttrType; } protected CorrelationCase getCorrelationCaseFromId(int correlationCaseId) throws EamDbException { @@ -56,4 +62,5 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS } throw new IllegalArgumentException("Cannot locate case."); } + } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form index 8c62356803..bfe2c9823c 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form @@ -24,11 +24,17 @@ - - - - + + + + + + + + + + @@ -42,6 +48,11 @@ + + + + + @@ -85,5 +96,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java index 8fc4efd01e..a86521411a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java @@ -21,9 +21,13 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.ComboBoxModel; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; /** * UI controls for Common Files Search scenario where the user intends to find @@ -43,6 +47,8 @@ public class InterCasePanel extends javax.swing.JPanel { // false if we must find matches in a given case plus the current case private boolean anyCase; + private Map correlationTypeFilters; + /** * Creates new form InterCasePanel */ @@ -50,6 +56,8 @@ public class InterCasePanel extends javax.swing.JPanel { initComponents(); this.caseMap = new HashMap<>(); this.anyCase = true; + + } private void specificCaseSelected(boolean selected) { @@ -60,6 +68,25 @@ public class InterCasePanel extends javax.swing.JPanel { } } + /** + * If the EamDB is enabled, the UI will populate the correlation type ComboBox with + * available types in the CR. + */ + void setupCorrelationTypeFilter() { + this.correlationTypeFilters = new HashMap<>(); + try { + List types = CorrelationAttributeInstance.getDefaultCorrelationTypes(); + for (CorrelationAttributeInstance.Type type : types) { + correlationTypeFilters.put(type.getDisplayName(), type); + this.correlationTypeComboBox.addItem(type.getDisplayName()); + } + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + } + this.correlationTypeComboBox.setSelectedIndex(0); + + } + /** * 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 @@ -73,6 +100,8 @@ public class InterCasePanel extends javax.swing.JPanel { anyCentralRepoCaseRadio = new javax.swing.JRadioButton(); specificCentralRepoCaseRadio = new javax.swing.JRadioButton(); caseComboBox = new javax.swing.JComboBox<>(); + comboBoxLabel = new javax.swing.JLabel(); + correlationTypeComboBox = new javax.swing.JComboBox<>(); buttonGroup.add(anyCentralRepoCaseRadio); anyCentralRepoCaseRadio.setSelected(true); @@ -94,6 +123,11 @@ public class InterCasePanel extends javax.swing.JPanel { caseComboBox.setModel(casesList); caseComboBox.setEnabled(false); + org.openide.awt.Mnemonics.setLocalizedText(comboBoxLabel, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.comboBoxLabel.text")); // NOI18N + + correlationTypeComboBox.setSelectedItem(null); + correlationTypeComboBox.setToolTipText(org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.correlationTypeComboBox.toolTipText")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -102,10 +136,14 @@ public class InterCasePanel extends javax.swing.JPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(anyCentralRepoCaseRadio) + .addComponent(specificCentralRepoCaseRadio) .addGroup(layout.createSequentialGroup() .addGap(21, 21, 21) - .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(specificCentralRepoCaseRadio)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(comboBoxLabel) + .addComponent(correlationTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE))))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -115,7 +153,12 @@ public class InterCasePanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(specificCentralRepoCaseRadio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboBoxLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(correlationTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -137,6 +180,8 @@ public class InterCasePanel extends javax.swing.JPanel { private javax.swing.JRadioButton anyCentralRepoCaseRadio; private javax.swing.ButtonGroup buttonGroup; private javax.swing.JComboBox caseComboBox; + private javax.swing.JLabel comboBoxLabel; + private javax.swing.JComboBox correlationTypeComboBox; private javax.swing.JRadioButton specificCentralRepoCaseRadio; // End of variables declaration//GEN-END:variables @@ -181,4 +226,12 @@ public class InterCasePanel extends javax.swing.JPanel { return InterCasePanel.NO_CASE_SELECTED; } + + /** + * Returns the selected Correlation Type by getting the Type from the stored HashMap. + * @return Type the selected Correlation Type to query for. + */ + CorrelationAttributeInstance.Type getSelectedCorrelationType() { + return correlationTypeFilters.get(this.correlationTypeComboBox.getSelectedItem().toString()); + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java index 16ee2bd23a..d258db2cc1 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java @@ -26,10 +26,13 @@ import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskData; @@ -42,46 +45,87 @@ import org.sleuthkit.datamodel.HashUtility; final class InterCaseSearchResultsProcessor { private Map dataSources; - + + /** + * The CorrelationAttributeInstance.Type this Processor will query on + */ + private final Type correlationType; + private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); - private final String interCaseWhereClause = "value IN (SELECT value FROM file_instances" - + " WHERE value IN (SELECT value FROM file_instances" - + " WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)" - + " GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"; - - private final String singleInterCaseWhereClause = "value IN (SELECT value FROM file_instances " - + "WHERE value IN (SELECT value FROM file_instances " - + "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) " - + "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"; - /** - * Used in the InterCaseCommonAttributeSearchers to find common attribute instances and generate nodes at the UI level. - * @param dataSources + * The initial CorrelationAttributeInstance ids lookup query. */ - InterCaseSearchResultsProcessor(Map dataSources){ + private final String interCaseWhereClause; + + /** + * The single CorrelationAttributeInstance object retrieval query + */ + private final String singleInterCaseWhereClause; + + /** + * Used in the InterCaseCommonAttributeSearchers to find common attribute + * instances and generate nodes at the UI level. + * + * @param dataSources the cases to filter and correlate on + * @param theType the type of CR data to search + */ + InterCaseSearchResultsProcessor(Map dataSources, CorrelationAttributeInstance.Type theType) { + this.correlationType = theType; this.dataSources = dataSources; + interCaseWhereClause = getInterCaseWhereClause(); + singleInterCaseWhereClause = getSingleInterCaseWhereClause(); } - + + private String getInterCaseWhereClause() { + String tableName = EamDbUtil.correlationTypeToInstanceTableName(correlationType); + StringBuilder sqlString = new StringBuilder(250); + sqlString.append("value IN (SELECT value FROM ") + .append(tableName) + .append(" WHERE value IN (SELECT value FROM ") + .append(tableName) + .append(" WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)") + .append(" GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"); + return sqlString.toString(); + } + + private String getSingleInterCaseWhereClause() { + String tableName = EamDbUtil.correlationTypeToInstanceTableName(correlationType); + StringBuilder sqlString = new StringBuilder(250); + sqlString.append("value IN (SELECT value FROM ") + .append(tableName) + .append(" WHERE value IN (SELECT value FROM ") + .append(tableName) + .append(" WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)") + .append(" AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"); + return sqlString.toString(); + } + /** - * Used in the CentralRepoCommonAttributeInstance to find common attribute instances and generate nodes at the UI level. + * Used in the CentralRepoCommonAttributeInstance to find common attribute + * instances and generate nodes at the UI level. + * + * @param theType the type of CR data to search */ - InterCaseSearchResultsProcessor(){ - //intentionally emtpy - we need a constructor which does not set the data sources field + InterCaseSearchResultsProcessor(CorrelationAttributeInstance.Type theType) { + this.correlationType = theType; + interCaseWhereClause = getInterCaseWhereClause(); + singleInterCaseWhereClause = getSingleInterCaseWhereClause(); } - + /** * Finds a single CorrelationAttribute given an id. * * @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb + * * @return CorrelationAttribute object representation of retrieved match */ CorrelationAttributeInstance findSingleCorrelationAttribute(int attrbuteId) { try { + InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); - DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback); + DbManager.processInstanceTableWhere(correlationType, String.format("id = %s", attrbuteId), instancetableCallback); return instancetableCallback.getCorrelationAttribute(); @@ -102,15 +146,15 @@ final class InterCaseSearchResultsProcessor { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); + int caseId = DbManager.getCase(currentCase).getID(); - - DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId, + + DbManager.processInstanceTableWhere(correlationType, String.format(interCaseWhereClause, caseId, TskData.FileKnown.KNOWN.getFileKnownValue()), instancetableCallback); - + return instancetableCallback.getInstanceCollatedCommonFiles(); - + } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); } @@ -123,16 +167,15 @@ final class InterCaseSearchResultsProcessor { * md5 and case. * * @param currentCase The current TSK Case. - * @param singleCase The case of interest. Matches must exist in this case. + * @param singleCase The case of interest. Matches must exist in this case. */ Map findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); - CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); int caseId = DbManager.getCase(currentCase).getID(); int targetCaseId = singleCase.getID(); - DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId, + DbManager.processInstanceTableWhere(correlationType, String.format(singleInterCaseWhereClause, caseId, TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback); return instancetableCallback.getInstanceCollatedCommonFiles(); } catch (EamDbException ex) { @@ -158,27 +201,42 @@ final class InterCaseSearchResultsProcessor { while (resultSet.next()) { int resultId = InstanceTableCallback.getId(resultSet); - String md5Value = InstanceTableCallback.getValue(resultSet); + String corValue = InstanceTableCallback.getValue(resultSet); if (previousRowMd5.isEmpty()) { - previousRowMd5 = md5Value; + previousRowMd5 = corValue; } - if (md5Value == null || HashUtility.isNoDataMd5(md5Value)) { + if (corValue == null || HashUtility.isNoDataMd5(corValue)) { continue; } - countAndAddCommonAttributes(md5Value, resultId); + countAndAddCommonAttributes(corValue, resultId); } + //Add the final instances + CommonAttributeValueList value = new CommonAttributeValueList(); + if (commonAttributeValue != null) { + value.addMetadataToList(commonAttributeValue); + instanceCollatedCommonFiles.put(commonAttributeValue.getInstanceCount(), value); + } } catch (SQLException ex) { LOGGER.log(Level.WARNING, "Error getting artifact instances from database.", ex); // NON-NLS } } - private void countAndAddCommonAttributes(String md5Value, int resultId) { + /** + * Add a resultId to the list of matches for a given corValue, which + * counts to number of instances of that match, determining which + * InstanceCountNode the match will be added to. + * + * @param corValue the value which matches + * @param resultId the CorrelationAttributeInstance id to be retrieved + * later. + */ + private void countAndAddCommonAttributes(String corValue, int resultId) { if (commonAttributeValue == null) { - commonAttributeValue = new CommonAttributeValue(md5Value); + commonAttributeValue = new CommonAttributeValue(corValue); } - if (!md5Value.equals(previousRowMd5)) { + if (!corValue.equals(previousRowMd5)) { int size = commonAttributeValue.getInstanceCount(); if (instanceCollatedCommonFiles.containsKey(size)) { instanceCollatedCommonFiles.get(size).addMetadataToList(commonAttributeValue); @@ -188,13 +246,13 @@ final class InterCaseSearchResultsProcessor { instanceCollatedCommonFiles.put(size, value); } - commonAttributeValue = new CommonAttributeValue(md5Value); - previousRowMd5 = md5Value; + commonAttributeValue = new CommonAttributeValue(corValue); + previousRowMd5 = corValue; } // we don't *have* all the information for the rows in the CR, // so we need to consult the present case via the SleuthkitCase object // Later, when the FileInstanceNode is built. Therefore, build node generators for now. - AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources); + AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources, correlationType); commonAttributeValue.addInstance(searchResult); } @@ -215,16 +273,19 @@ final class InterCaseSearchResultsProcessor { public void process(ResultSet resultSet) { try { EamDb DbManager = EamDb.getInstance(); - CorrelationAttributeInstance.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); while (resultSet.next()) { CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet)); CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); - correlationAttributeInstance = DbManager.getCorrelationAttributeInstance(fileType, - correlationCase, - dataSource, - InstanceTableCallback.getValue(resultSet), - InstanceTableCallback.getFilePath(resultSet)); + try { + correlationAttributeInstance = DbManager.getCorrelationAttributeInstance(correlationType, + correlationCase, + dataSource, + InstanceTableCallback.getValue(resultSet), + InstanceTableCallback.getFilePath(resultSet)); + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, "Unable to get CorrelationAttributeInstance.", ex); // NON-NLS + } } } catch (SQLException | EamDbException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index f02f26b869..569ae5232f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -36,8 +36,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * * Generates a List when - * findFiles() is called, which organizes files by md5 to - * prepare to display in viewer. + * findMatches() is called, which organizes files by md5 to prepare + * to display in viewer. * * This entire thing runs on a background thread where exceptions are handled. */ @@ -93,14 +93,14 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt * @throws SQLException */ @Override - public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException { + public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException { Map commonFiles = new HashMap<>(); - + final Case currentCase = Case.getCurrentCaseThrows(); final String caseName = currentCase.getDisplayName(); SleuthkitCase sleuthkitCase = currentCase.getSleuthkitCase(); - + String selectStatement = this.buildSqlSelectStatement(); try ( @@ -127,21 +127,21 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt } } } - + Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); - + return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } /** - * Should be used by subclasses, in their - * buildSqlSelectStatement() function to create an SQL boolean - * expression which will filter our matches based on mime type. The + * Should be used by subclasses, in their + * buildSqlSelectStatement() function to create an SQL boolean + * expression which will filter our matches based on mime type. The * expression will be conjoined to base query with an AND operator. - * - * @return sql fragment of the form: - * 'and "mime_type" in ( [comma delimited list of mime types] )' - * or empty string in the event that no types to filter on were given. + * + * @return sql fragment of the form: 'and "mime_type" in ( [comma delimited + * list of mime types] )' or empty string in the event that no types to + * filter on were given. */ String determineMimeTypeFilter() { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index 5bed8625a0..8091e96fbb 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -20,42 +20,44 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.sql.SQLException; -import java.util.List; import java.util.Map; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; /** - * - * + * + * */ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher { - + private final int corrleationCaseId; private String correlationCaseName; - + /** - * + * * @param correlationCaseId * @param filterByMediaMimeType * @param filterByDocMimeType - * @throws EamDbException + * @throws EamDbException */ - public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { - super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType, percentageThreshold); - + public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, + boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold); + this.corrleationCaseId = correlationCaseId; this.correlationCaseName = ""; } - + /** - * Collect metadata required to render the tree table where matches must + * Collect metadata required to render the tree table where matches must * occur in the case with the given ID. - * - * @param correlationCaseId id of case where matches must occur (no other matches will be shown) + * + * @param correlationCaseId id of case where matches must occur (no other + * matches will be shown) * @return business object needed to populate tree table with results * @throws TskCoreException * @throws NoCurrentCaseException @@ -63,24 +65,23 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri * @throws EamDbException */ @Override - public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - + public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + CorrelationCase cCase = this.getCorrelationCaseFromId(this.corrleationCaseId); - correlationCaseName = cCase.getDisplayName(); + this.correlationCaseName = cCase.getDisplayName(); return this.findFiles(cCase); } CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), this.corAttrType); Map interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); - return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType); } - + @Override String buildTabTitle() { - final String buildCategorySelectionString = this.buildCategorySelectionString(); final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterSingle(); - return String.format(titleTemplate, new Object[]{correlationCaseName, buildCategorySelectionString}); + return String.format(titleTemplate, new Object[]{this.correlationCaseName, this.corAttrType.getDisplayName()}); } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 2fd30addcc..0b92830002 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -18,10 +18,12 @@ */ package org.sleuthkit.autopsy.communications; +import java.util.List; import java.util.TimeZone; import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; @@ -37,6 +39,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME; +import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; @@ -57,6 +60,7 @@ final class RelationshipNode extends BlackboardArtifactNode { @Override protected Sheet createSheet() { Sheet sheet = new Sheet(); + List tags = getAllTagsFromDatabase(); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if (sheetSet == null) { sheetSet = Sheet.createPropertiesSet(); @@ -64,7 +68,10 @@ final class RelationshipNode extends BlackboardArtifactNode { } sheetSet.put(new NodeProperty<>("Type", "Type", "Type", getDisplayName())); - + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); final BlackboardArtifact artifact = getArtifact(); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(getArtifact().getArtifactTypeID()); if (null != fromID) { @@ -113,8 +120,7 @@ final class RelationshipNode extends BlackboardArtifactNode { break; } } - - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.form new file mode 100755 index 0000000000..4fe61b2fb5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.form @@ -0,0 +1,50 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java new file mode 100755 index 0000000000..f4f1d0380c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java @@ -0,0 +1,466 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.contentviewers; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.apache.commons.lang3.StringEscapeUtils; + +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.openide.nodes.Node; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Annotations view of file contents. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +@ServiceProvider(service = DataContentViewer.class, position = 8) +@NbBundle.Messages({ + "AnnotationsContentViewer.title=Annotations", + "AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content." +}) +public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer { + + private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName()); + + /** + * Creates an instance of AnnotationsContentViewer. + */ + public AnnotationsContentViewer() { + initComponents(); + Utilities.configureTextPaneAsHtml(jTextPane1); + } + + @Override + public void setNode(Node node) { + if ((node == null) || (!isSupported(node))) { + resetComponent(); + return; + } + + StringBuilder html = new StringBuilder(); + + BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); + Content sourceFile = null; + + try { + if (artifact != null) { + /* + * Get the source content based on the artifact to ensure we + * display the correct data instead of whatever was in the node. + */ + sourceFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()); + } else { + /* + * No artifact is present, so get the content based on what's + * present in the node. In this case, the selected item IS the + * source file. + */ + sourceFile = node.getLookup().lookup(AbstractFile.class); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format( + "Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).", + artifact.getDisplayName(), artifact.getArtifactID()), ex); + } + + if (artifact != null) { + populateTagData(html, artifact, sourceFile); + } else { + populateTagData(html, sourceFile); + } + + if (sourceFile instanceof AbstractFile) { + populateCentralRepositoryData(html, artifact, (AbstractFile) sourceFile); + } + + setText(html.toString()); + jTextPane1.setCaretPosition(0); + } + + /** + * Populate the "Selected Item" sections with tag data for the supplied + * content. + * + * @param html The HTML text to update. + * @param content Selected content. + */ + private void populateTagData(StringBuilder html, Content content) { + try { + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + + startSection(html, "Selected Item"); + List fileTagsList = tskCase.getContentTagsByContent(content); + if (fileTagsList.isEmpty()) { + addMessage(html, "There are no tags for the selected content."); + } else { + for (ContentTag tag : fileTagsList) { + addTagEntry(html, tag); + } + } + endSection(html); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS + } + } + + /** + * Populate the "Selected Item" and "Source File" sections with tag data for + * a supplied artifact. + * + * @param html The HTML text to update. + * @param artifact A selected artifact. + * @param sourceFile The source content of the selected artifact. + */ + private void populateTagData(StringBuilder html, BlackboardArtifact artifact, Content sourceFile) { + try { + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + + startSection(html, "Selected Item"); + List artifactTagsList = tskCase.getBlackboardArtifactTagsByArtifact(artifact); + if (artifactTagsList.isEmpty()) { + addMessage(html, "There are no tags for the selected artifact."); + } else { + for (BlackboardArtifactTag tag : artifactTagsList) { + addTagEntry(html, tag); + } + } + endSection(html); + + if (sourceFile != null) { + startSection(html, "Source File"); + List fileTagsList = tskCase.getContentTagsByContent(sourceFile); + if (fileTagsList.isEmpty()) { + addMessage(html, "There are no tags for the source content."); + } else { + for (ContentTag tag : fileTagsList) { + addTagEntry(html, tag); + } + } + endSection(html); + } + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS + } + } + + /** + * Populate the "Central Repository Comments" section with data. + * + * @param html The HTML text to update. + * @param artifact A selected artifact (can be null). + * @param sourceFile A selected file, or a source file of the selected + * artifact. + */ + private void populateCentralRepositoryData(StringBuilder html, BlackboardArtifact artifact, AbstractFile sourceFile) { + if (EamDb.isEnabled()) { + startSection(html, "Central Repository Comments"); + List instancesList = new ArrayList<>(); + if (artifact != null) { + instancesList.addAll(EamArtifactUtil.makeInstancesFromBlackboardArtifact(artifact, false)); + } + try { + List artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); + String md5 = sourceFile.getMd5Hash(); + if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) { + for (CorrelationAttributeInstance.Type attributeType : artifactTypes) { + if (attributeType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + instancesList.add(new CorrelationAttributeInstance( + md5, + attributeType, + correlationCase, + CorrelationDataSource.fromTSKDataSource(correlationCase, sourceFile.getDataSource()), + sourceFile.getParentPath() + sourceFile.getName(), + "", + sourceFile.getKnown())); + break; + } + } + } + + boolean commentDataFound = false; + + for (CorrelationAttributeInstance instance : instancesList) { + List correlatedInstancesList = + EamDb.getInstance().getArtifactInstancesByTypeValue(instance.getCorrelationType(), instance.getCorrelationValue()); + for (CorrelationAttributeInstance correlatedInstance : correlatedInstancesList) { + if (correlatedInstance.getComment() != null && correlatedInstance.getComment().isEmpty() == false) { + commentDataFound = true; + addCentralRepositoryEntry(html, correlatedInstance); + } + } + } + + if (commentDataFound == false) { + addMessage(html, "There is no comment data for the selected content in the central repository."); + } + } catch (EamDbException | TskCoreException ex) { + logger.log(Level.SEVERE, "Error connecting to the central repository database.", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.SEVERE, "Error normalizing instance from repository database.", ex); // NON-NLS + } + endSection(html); + } + } + + /** + * Set the text of the text panel. + * + * @param text The text to set to the text panel. + */ + private void setText(String text) { + jTextPane1.setText("" + text + ""); //NON-NLS + } + + /** + * Start a new data section. + * + * @param html The HTML text to add the section to. + * @param sectionName The name of the section. + */ + private void startSection(StringBuilder html, String sectionName) { + html.append("

") + .append(sectionName) + .append("


"); //NON-NLS + } + + /** + * Add a message. + * + * @param html The HTML text to add the message to. + * @param message The message text. + */ + private void addMessage(StringBuilder html, String message) { + html.append("

") + .append(message) + .append("


"); //NON-NLS + } + + /** + * Add a data table containing information about a tag. + * + * @param html The HTML text to add the table to. + * @param tag The tag whose information will be used to populate the table. + */ + @NbBundle.Messages({ + "AnnotationsContentViewer.tagEntryDataLabel.tag=Tag:", + "AnnotationsContentViewer.tagEntryDataLabel.tagUser=Tag User:", + "AnnotationsContentViewer.tagEntryDataLabel.comment=Comment:" + }) + private void addTagEntry(StringBuilder html, Tag tag) { + startTable(html); + addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tag(), tag.getName().getDisplayName()); + addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tagUser(), tag.getUserName()); + addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), formatHtmlString(tag.getComment())); + endTable(html); + } + + /** + * Add a data table containing information about a correlation attribute + * instance in the central repository. + * + * @param html The HTML text to add the table to. + * @param attributeInstance The attribute instance whose information will be + * used to populate the table. + * @param correlationType The correlation data type. + */ + @NbBundle.Messages({ + "AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:", + "AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type:", + "AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:", + "AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:" + }) + private void addCentralRepositoryEntry(StringBuilder html, CorrelationAttributeInstance attributeInstance) { + startTable(html); + addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_case(), attributeInstance.getCorrelationCase().getDisplayName()); + addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_type(), attributeInstance.getCorrelationType().getDisplayName()); + addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_comment(), formatHtmlString(attributeInstance.getComment())); + addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), attributeInstance.getFilePath()); + endTable(html); + } + + /** + * Start a data table. + * + * @param html The HTML text to add the table to. + */ + private void startTable(StringBuilder html) { + html.append(""); //NON-NLS + } + + /** + * Add a data row to a table. + * + * @param html The HTML text to add the row to. + * @param key The key for the left column of the data row. + * @param value The value for the right column of the data row. + */ + private void addRow(StringBuilder html, String key, String value) { + html.append(""); //NON-NLS + } + + /** + * End a data table. + * + * @param html The HTML text on which to end a table. + */ + private void endTable(StringBuilder html) { + html.append("
"); //NON-NLS + html.append(key); + html.append(""); //NON-NLS + html.append(value); + html.append("


"); //NON-NLS + } + + /** + * End a data section. + * + * @param html The HTML text on which to end a section. + */ + private void endSection(StringBuilder html) { + html.append("
"); //NON-NLS + } + + /** + * Apply escape sequence to special characters. Line feed and carriage + * return character combinations will be converted to HTML line breaks. + * + * @param text The text to format. + * @return The formatted text. + */ + private String formatHtmlString(String text) { + String formattedString = StringEscapeUtils.escapeHtml4(text); + return formattedString.replaceAll("(\r\n|\r|\n|\n\r)", "
"); + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane5 = new javax.swing.JScrollPane(); + jTextPane1 = new javax.swing.JTextPane(); + + setPreferredSize(new java.awt.Dimension(100, 58)); + + jTextPane1.setEditable(false); + jTextPane1.setName(""); // NOI18N + jTextPane1.setPreferredSize(new java.awt.Dimension(600, 52)); + jScrollPane5.setViewportView(jTextPane1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane5, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane5, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane jScrollPane5; + private javax.swing.JTextPane jTextPane1; + // End of variables declaration//GEN-END:variables + + @Override + public String getTitle() { + return Bundle.AnnotationsContentViewer_title(); + } + + @Override + public String getToolTip() { + return Bundle.AnnotationsContentViewer_toolTip(); + } + + @Override + public DataContentViewer createInstance() { + return new AnnotationsContentViewer(); + } + + @Override + public boolean isSupported(Node node) { + BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); + + try { + if (artifact != null) { + if (artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()) != null) { + return true; + } + } else { + if (node.getLookup().lookup(AbstractFile.class) != null) { + return true; + } + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format( + "Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).", + artifact.getDisplayName(), artifact.getArtifactID()), ex); + } + + return false; + } + + @Override + public int isPreferred(Node node) { + return 1; + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + setText(""); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties index 38f772cbf0..b033af82f9 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties @@ -46,4 +46,4 @@ Metadata.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3 Metadata.tableRowTitle.type=\u30bf\u30a4\u30d7 Metadata.nodeText.exceptionNotice.text=\u30d5\u30a1\u30a4\u30eb\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\uff1a Metadata.nodeText.text=Sleuth Kit istat\u30c4\u30fc\u30eb\u304b\u3089\uff1a -Metadata.nodeText.nonFilePassedIn=\u5165\u529b\u3055\u308c\u305f\u3082\u306e\u306f\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093 \ No newline at end of file +Metadata.nodeText.nonFilePassedIn=\u5165\u529b\u3055\u308c\u305f\u3082\u306e\u306f\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093 diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index a95dd307f5..b109b12da0 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -36,6 +36,7 @@ import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -65,6 +66,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskCoreException; /** @@ -723,13 +725,19 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } + List tags = getContentTagsFromDatabase(); + AbstractFile file = getContent(); sheetSet.put(new NodeProperty<>("Name", "Name", "Name", file.getName())); + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); sheetSet.put(new NodeProperty<>("Size", "Size", "Size", file.getSize())); sheetSet.put(new NodeProperty<>("Mime Type", "Mime Type", "Mime Type", StringUtils.defaultString(file.getMIMEType()))); sheetSet.put(new NodeProperty<>("Known", "Known", "Known", file.getKnown().getName())); - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } } diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index 342b58c4ee..8bdde0f317 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -77,7 +77,7 @@ - + @@ -198,7 +198,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 024686e26f..0752b67075 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -26,6 +26,7 @@ import java.awt.Graphics; import java.awt.dnd.DnDConstants; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.beans.FeatureDescriptor; import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -37,8 +38,10 @@ import java.util.TreeMap; import java.util.TreeSet; import java.util.logging.Level; import java.util.prefs.Preferences; +import javax.swing.ImageIcon; import javax.swing.JTable; import javax.swing.ListSelectionModel; +import static javax.swing.SwingConstants.CENTER; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; @@ -58,12 +61,14 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; +import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; /** @@ -82,6 +87,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(DataResultViewerTable.class.getName()); + + private static final String NOTEPAD_ICON_PATH = "org/sleuthkit/autopsy/images/notepad16.png"; + private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; + private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; + private static final ImageIcon COMMENT_ICON = new ImageIcon(ImageUtilities.loadImage(NOTEPAD_ICON_PATH, false)); + private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); + private static final ImageIcon NOTABLE_ICON_SCORE = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name") static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl(); static private final Color TAGGED_ROW_COLOR = new Color(255, 255, 195); @@ -90,6 +102,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private final Map> propertiesMap; private final Outline outline; private final TableListener outlineViewListener; + private final IconRendererTableListener iconRendererListener; private Node rootNode; /** @@ -102,7 +115,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { public DataResultViewerTable() { this(null, Bundle.DataResultViewerTable_title()); } - /** * Constructs a tabular result viewer that displays the children of a given @@ -157,6 +169,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { outlineViewListener = new TableListener(); outline.getColumnModel().addColumnModelListener(outlineViewListener); + iconRendererListener = new IconRendererTableListener(); + outline.getColumnModel().addColumnModelListener(iconRendererListener); + /* * Add a mouse listener to the child OutlineView (explorer view) to make * sure the first column of the table is kept in place. @@ -180,15 +195,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer { /** * Gets the title of this tabular result viewer. - * @return + * + * @return title of tab. */ @Override @NbBundle.Messages("DataResultViewerTable.title=Table") public String getTitle() { return title; } - - + /** * Indicates whether a given node is supported as a root node for this * tabular viewer. @@ -210,11 +225,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { @Override @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void setNode(Node rootNode) { - if (! SwingUtilities.isEventDispatchThread()) { + if (!SwingUtilities.isEventDispatchThread()) { LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread"); return; } - + /* * The quick filter must be reset because when determining column width, * ETable.getRowCount is called, and the documentation states that quick @@ -254,9 +269,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } /** - * Sets up the Outline view of this tabular result viewer by creating - * column headers based on the children of the current root node. The - * persisted column order, sorting and visibility is used. + * Sets up the Outline view of this tabular result viewer by creating column + * headers based on the children of the current root node. The persisted + * column order, sorting and visibility is used. */ private void setupTable() { /* @@ -288,7 +303,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * let the table resize itself. */ outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF); - + assignColumns(props); // assign columns to match the properties if (firstProp != null) { ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName()); @@ -347,12 +362,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * treated as un-hide/hide. */ outlineViewListener.listenToVisibilityChanges(true); + } /* - * Populates the column map for the child OutlineView of this tabular - * result viewer with references to the column objects for use when - * loading/storing the visibility info. + * Populates the column map for the child OutlineView of this tabular result + * viewer with references to the column objects for use when loading/storing + * the visibility info. */ private void populateColumnMap() { columnMap.clear(); @@ -364,6 +380,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { if (entry.getKey() < columnCount) { final ETableColumn column = (ETableColumn) columnModel.getColumn(entry.getKey()); columnMap.put(propName, column); + } } } @@ -408,8 +425,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); } } - - protected TableColumnModel getColumnModel(){ + + protected TableColumnModel getColumnModel() { return outline.getColumnModel(); } @@ -640,6 +657,57 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } + /** + * Listener which sets the custom icon renderer on columns which contain + * icons instead of text when a column is added. + */ + private class IconRendererTableListener implements TableColumnModelListener { + + @NbBundle.Messages({"DataResultViewerTable.commentRender.name=C", + "DataResultViewerTable.scoreRender.name=S", + "DataResultViewerTable.countRender.name=O"}) + @Override + public void columnAdded(TableColumnModelEvent e) { + if (e.getSource() instanceof ETableColumnModel) { + TableColumn column = ((TableColumnModel) e.getSource()).getColumn(e.getToIndex()); + if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_commentRender_name())) { + //if the current column is a comment column set the cell renderer to be the HasCommentCellRenderer + column.setCellRenderer(new HasCommentCellRenderer()); + } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_scoreRender_name())) { + //if the current column is a score column set the cell renderer to be the ScoreCellRenderer + column.setCellRenderer(new ScoreCellRenderer()); + } else if (column.getHeaderValue().toString().equals(Bundle.DataResultViewerTable_countRender_name())) { + column.setCellRenderer(new CountCellRenderer()); + } + } + } + + @Override + public void columnRemoved(TableColumnModelEvent e + ) { + //Don't do anything when column removed + } + + @Override + public void columnMoved(TableColumnModelEvent e + ) { + //Don't do anything when column moved + } + + @Override + public void columnMarginChanged(ChangeEvent e + ) { + //Don't do anything when column margin changed + } + + @Override + public void columnSelectionChanged(ListSelectionEvent e + ) { + //Don't do anything when column selection changed + } + + } + /** * Listens to mouse events and table column events and persists column order * sorting, and visibility changes. @@ -801,6 +869,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { try { tagFound = !prop.getValue().equals(""); } catch (IllegalAccessException | InvocationTargetException ignore) { + //if unable to get the tags property value, treat it like it not having a comment } break; } @@ -816,6 +885,174 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } + /* + * A renderer which based on the contents of the cell will display an icon + * to indicate the presence of a comment related to the content. + */ + private final class HasCommentCellRenderer extends ColorTagCustomRenderer { + + private static final long serialVersionUID = 1L; + + @NbBundle.Messages({"DataResultViewerTable.commentRenderer.crComment.toolTip=Comment exists in Central Repository", + "DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)", + "DataResultViewerTable.commentRenderer.crAndTagComment.toolTip=Comments exist both in Central Repository and on associated tag(s)", + "DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"}) + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBackground(component.getBackground()); //inherit highlighting + setHorizontalAlignment(CENTER); + Object switchValue = null; + if ((value instanceof NodeProperty)) { + //The Outline view has properties in the cell, the value contained in the property is what we want + try { + switchValue = ((Node.Property) value).getValue(); + } catch (IllegalAccessException | InvocationTargetException ex) { + //Unable to get the value from the NodeProperty no Icon will be displayed + } + } else { + //JTables contain the value we want directly in the cell + switchValue = value; + } + setText(""); + if ((switchValue instanceof HasCommentStatus)) { + + switch ((HasCommentStatus) switchValue) { + case CR_COMMENT: + setIcon(COMMENT_ICON); + setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crComment_toolTip()); + break; + case TAG_COMMENT: + setIcon(COMMENT_ICON); + setToolTipText(Bundle.DataResultViewerTable_commentRenderer_tagComment_toolTip()); + break; + case CR_AND_TAG_COMMENTS: + setIcon(COMMENT_ICON); + setToolTipText(Bundle.DataResultViewerTable_commentRenderer_crAndTagComment_toolTip()); + break; + case TAG_NO_COMMENT: + case NO_COMMENT: + default: + setIcon(null); + setToolTipText(Bundle.DataResultViewerTable_commentRenderer_noComment_toolTip()); + } + } else { + setIcon(null); + } + + return this; + } + + } + + /* + * A renderer which based on the contents of the cell will display an icon + * to indicate the score associated with the item. + */ + private final class ScoreCellRenderer extends ColorTagCustomRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBackground(component.getBackground()); //inherit highlighting + setHorizontalAlignment(CENTER); + Object switchValue = null; + if ((value instanceof NodeProperty)) { + //The Outline view has properties in the cell, the value contained in the property is what we want + try { + switchValue = ((Node.Property) value).getValue(); + setToolTipText(((FeatureDescriptor) value).getShortDescription()); + } catch (IllegalAccessException | InvocationTargetException ex) { + //Unable to get the value from the NodeProperty no Icon will be displayed + } + + } else { + //JTables contain the value we want directly in the cell + switchValue = value; + } + setText(""); + if ((switchValue instanceof Score)) { + + switch ((Score) switchValue) { + case INTERESTING_SCORE: + setIcon(INTERESTING_SCORE_ICON); + break; + case NOTABLE_SCORE: + setIcon(NOTABLE_ICON_SCORE); + break; + case NO_SCORE: + default: + setIcon(null); + } + } else { + setIcon(null); + } + return this; + } + + } + + /* + * A renderer which based on the contents of the cell will display an empty + * cell if no count was available. + */ + private final class CountCellRenderer extends ColorTagCustomRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBackground(component.getBackground()); //inherit highlighting + setHorizontalAlignment(LEFT); + Object countValue = null; + if ((value instanceof NodeProperty)) { + //The Outline view has properties in the cell, the value contained in the property is what we want + try { + countValue = ((Node.Property) value).getValue(); + setToolTipText(((FeatureDescriptor) value).getShortDescription()); + } catch (IllegalAccessException | InvocationTargetException ex) { + //Unable to get the value from the NodeProperty no Icon will be displayed + } + } else { + //JTables contain the value we want directly in the cell + countValue = value; + } + setText(""); + if ((countValue instanceof Long)) { + //Don't display value if value is negative used so that sorting will behave as desired + if ((Long) countValue >= 0) { + setText(countValue.toString()); + } + } + return this; + } + + } + + /** + * Enum to denote the presence of a comment associated with the content or + * artifacts generated from it. + */ + public enum HasCommentStatus { + NO_COMMENT, + TAG_NO_COMMENT, + CR_COMMENT, + TAG_COMMENT, + CR_AND_TAG_COMMENTS + } + + /** + * Enum to denote the score given to an item to draw the users attention + */ + public enum Score { + NO_SCORE, + INTERESTING_SCORE, + NOTABLE_SCORE + } + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index b4f8f76728..204446ba24 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -34,17 +34,28 @@ import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * An abstract node that encapsulates AbstractFile data @@ -58,7 +69,7 @@ public abstract class AbstractAbstractFileNode extends A private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc(); private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, - Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED); + Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED); /** * @param abstractFile file to wrap @@ -145,6 +156,11 @@ public abstract class AbstractAbstractFileNode extends A if (event.getDeletedTagInfo().getContentID() == content.getId()) { updateSheet(); } + } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { + CommentChangedEvent event = (CommentChangedEvent) evt; + if (event.getContentID() == content.getId()) { + updateSheet(); + } } }; @@ -253,6 +269,133 @@ public abstract class AbstractAbstractFileNode extends A map.put(EXTENSION.toString(), content.getNameExtension()); } + /** + * Get all tags from the case database that are associated with the file + * + * @return a list of tags that are associated with the file + */ + protected final List getContentTagsFromDatabase() { + List tags = new ArrayList<>(); + try { + tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content)); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex); + } + return tags; + } + + protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { + CorrelationAttributeInstance correlationAttribute = null; + if (EamDbUtil.useCentralRepo()) { + correlationAttribute = EamArtifactUtil.getInstanceFromContent(content); + } + return correlationAttribute; + } + + /** + * Used by subclasses of AbstractAbstractFileNode to add the comment + * property to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags associated with the file + * @param attribute the correlation attribute associated with this file, + * null if central repo is not enabled + */ + @NbBundle.Messages({"AbstractAbstractFileNode.createSheet.comment.name=C", + "AbstractAbstractFileNode.createSheet.comment.displayName=C"}) + protected final void addCommentProperty(Sheet.Set sheetSet, List tags, CorrelationAttributeInstance attribute) { + + HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT; + + for (ContentTag tag : tags) { + if (!StringUtils.isBlank(tag.getComment())) { + //if the tag is null or empty or contains just white space it will indicate there is not a comment + status = HasCommentStatus.TAG_COMMENT; + break; + } + } + if (attribute != null && !StringUtils.isBlank(attribute.getComment())) { + if (status == HasCommentStatus.TAG_COMMENT) { + status = HasCommentStatus.CR_AND_TAG_COMMENTS; + } else { + status = HasCommentStatus.CR_COMMENT; + } + } + sheetSet.put(new NodeProperty<>(AbstractAbstractFileNode_createSheet_comment_name(), AbstractAbstractFileNode_createSheet_comment_displayName(), NO_DESCR, + status)); + } + + /** + * Used by subclasses of AbstractAbstractFileNode to add the Score property + * to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags associated with the file + */ + @NbBundle.Messages({"AbstractAbstractFileNode.createSheet.score.name=S", + "AbstractAbstractFileNode.createSheet.score.displayName=S", + "AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.", + "AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.", + "AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.", + "AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag."}) + protected final void addScoreProperty(Sheet.Set sheetSet, List tags) { + Score score = Score.NO_SCORE; + String description = NO_DESCR; + if (content.getKnown() == TskData.FileKnown.BAD) { + score = Score.NOTABLE_SCORE; + description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description(); + } + try { + if (score == Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) { + score = Score.INTERESTING_SCORE; + description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting artifacts for file: " + content.getName(), ex); + } + if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) { + score = Score.INTERESTING_SCORE; + description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description(); + for (ContentTag tag : tags) { + if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { + score = Score.NOTABLE_SCORE; + description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description(); + break; + } + } + } + sheetSet.put(new NodeProperty<>(Bundle.AbstractAbstractFileNode_createSheet_score_name(), Bundle.AbstractAbstractFileNode_createSheet_score_displayName(), description, score)); + } + + @NbBundle.Messages({"AbstractAbstractFileNode.createSheet.count.name=O", + "AbstractAbstractFileNode.createSheet.count.displayName=O", + "AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated", + "AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated", + "# {0} - occuranceCount", + "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"}) + protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) { + Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting + String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description(); + try { + //don't perform the query if there is no correlation value + if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) { + count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue()); + description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count); + } else if (attribute != null) { + description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description(); + } + } catch (EamDbException ex) { + logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex); + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex); + } + + sheetSet.put( + new NodeProperty<>(Bundle.AbstractAbstractFileNode_createSheet_count_name(), Bundle.AbstractAbstractFileNode_createSheet_count_displayName(), description, count)); + } + /** * Used by subclasses of AbstractAbstractFileNode to add the tags property * to their sheets. @@ -261,6 +404,7 @@ public abstract class AbstractAbstractFileNode extends A * Sheet.get(Sheet.PROPERTIES) */ @NbBundle.Messages("AbstractAbstractFileNode.tagsProperty.displayName=Tags") + @Deprecated protected void addTagProperty(Sheet.Set sheetSet) { List tags = new ArrayList<>(); try { @@ -274,6 +418,21 @@ public abstract class AbstractAbstractFileNode extends A .collect(Collectors.joining(", ")))); } + /** + * Used by subclasses of AbstractAbstractFileNode to add the tags property + * to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags associated with the file + */ + protected final void addTagProperty(Sheet.Set sheetSet, List tags) { + sheetSet.put(new NodeProperty<>("Tags", AbstractAbstractFileNode_tagsProperty_displayName(), + NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()) + .distinct() + .collect(Collectors.joining(", ")))); + } + private static String getContentPath(AbstractFile file) { try { return file.getUniquePath(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractFsContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractFsContentNode.java index 824407c417..02dd3c1d28 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractFsContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractFsContentNode.java @@ -19,11 +19,14 @@ package org.sleuthkit.autopsy.datamodel; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.ContentTag; /** * Abstract class that implements the commonality between File and Directory @@ -70,11 +73,21 @@ public abstract class AbstractFsContentNode extends Abst sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + List tags = getContentTagsFromDatabase(); Map map = new LinkedHashMap<>(); fillPropertyMap(map, getContent()); - final String NO_DESCR = Bundle.AbstractFsContentNode_noDesc_text(); + //add the name property before the comment property to ensure it is first column + sheetSet.put(new NodeProperty<>(AbstractFilePropertyType.NAME.toString(), + AbstractFilePropertyType.NAME.toString(), + NO_DESCR, + getName())); + //add the cr status property before the propertyMap to ensure it is early in column order + addScoreProperty(sheetSet, tags); + //add the comment property before the propertyMap to ensure it is early in column order + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); for (AbstractFilePropertyType propType : AbstractFilePropertyType.values()) { final String propString = propType.toString(); sheetSet.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString))); @@ -84,7 +97,7 @@ public abstract class AbstractFsContentNode extends Abst } // add tags property to the sheet - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 5b20c92c6e..4178d2fafe 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -46,11 +46,20 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; @@ -61,6 +70,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Node wrapping a blackboard artifact object. This is generated from several @@ -73,7 +83,8 @@ public class BlackboardArtifactNode extends AbstractContentNode contentCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES). @@ -127,6 +138,11 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = getAllTagsFromDatabase(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if (sheetSet == null) { sheetSet = Sheet.createPropertiesSet(); @@ -332,6 +350,10 @@ public class BlackboardArtifactNode extends AbstractContentNode getAllTagsFromDatabase() { + List tags = new ArrayList<>(); + try { + tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); + tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(associated)); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex); + } + return tags; + } + /** * Used by (subclasses of) BlackboardArtifactNode to add the tags property * to their sheets. @@ -494,6 +533,7 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = new ArrayList<>(); @@ -507,6 +547,137 @@ public class BlackboardArtifactNode extends AbstractContentNode t.getName().getDisplayName()).collect(Collectors.joining(", ")))); } + /** + * Used by (subclasses of) BlackboardArtifactNode to add the tags property + * to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags which should appear as the value for the + * property + */ + protected final void addTagProperty(Sheet.Set sheetSet, List tags) { + sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), + NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", ")))); + } + + protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { + CorrelationAttributeInstance correlationAttribute = null; + if (EamDbUtil.useCentralRepo()) { + correlationAttribute = EamArtifactUtil.getInstanceFromContent(associated); + } + return correlationAttribute; + } + + /** + * Used by (subclasses of) BlackboardArtifactNode to add the comment + * property to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags associated with the file + * @param attribute the correlation attribute associated with this + * artifact's associated file, null if central repo is not + * enabled + */ + @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C", + "BlackboardArtifactNode.createSheet.comment.displayName=C"}) + protected final void addCommentProperty(Sheet.Set sheetSet, List tags, CorrelationAttributeInstance attribute) { + HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT; + for (Tag tag : tags) { + if (!StringUtils.isBlank(tag.getComment())) { + //if the tag is null or empty or contains just white space it will indicate there is not a comment + status = HasCommentStatus.TAG_COMMENT; + break; + } + } + //currently checks for a comment on the associated file in the central repo not the artifact itself + //what we want the column property to reflect should be revisted when we have added a way to comment + //on the artifact itself + if (attribute != null && !StringUtils.isBlank(attribute.getComment())) { + if (status == HasCommentStatus.TAG_COMMENT) { + status = HasCommentStatus.CR_AND_TAG_COMMENTS; + } else { + status = HasCommentStatus.CR_COMMENT; + } + } + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, + status)); + } + + /** + * Used by (subclasses of) BlackboardArtifactNode to add the Score property + * to their sheets. + * + * @param sheetSet the modifiable Sheet.Set returned by + * Sheet.get(Sheet.PROPERTIES) + * @param tags the list of tags associated with the file + */ + @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S", + "BlackboardArtifactNode.createSheet.score.displayName=S", + "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.", + "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.", + "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.", + "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag."}) + protected final void addScoreProperty(Sheet.Set sheetSet, List tags) { + Score score = Score.NO_SCORE; + String description = ""; + if (associated instanceof AbstractFile) { + if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) { + score = Score.NOTABLE_SCORE; + description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description(); + } + } + try { + if (score == Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) { + score = Score.INTERESTING_SCORE; + description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting artifacts for artifact: " + content.getName(), ex); + } + if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) { + score = Score.INTERESTING_SCORE; + description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description(); + for (Tag tag : tags) { + if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { + score = Score.NOTABLE_SCORE; + description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description(); + break; + } + } + } + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score)); + } + + @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O", + "BlackboardArtifactNode.createSheet.count.displayName=O", + "BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated", + "BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated", + "# {0} - occuranceCount", + "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"}) + + protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) { + Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting + String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description(); + try { + //don't perform the query if there is no correlation value + if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) { + count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue()); + description = Bundle.BlackboardArtifactNode_createSheet_count_description(count); + } else if (attribute != null) { + description = Bundle.BlackboardArtifactNode_createSheet_count_hashLookupNotRun_description(); + } + } catch (EamDbException ex) { + logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex); + } + catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex); + } + sheetSet.put( + new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count)); + } + private void updateSheet() { this.setSheet(createSheet()); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java index c7f20b5c28..247e7cd76e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java @@ -31,11 +31,13 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.TskData; @@ -79,6 +81,8 @@ public class LayoutFileNode extends AbstractAbstractFileNode { sheet.put(sheetSet); } + List tags = getContentTagsFromDatabase(); + Map map = new LinkedHashMap<>(); fillPropertyMap(map); @@ -86,14 +90,17 @@ public class LayoutFileNode extends AbstractAbstractFileNode { NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.name.desc"), getName())); - + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); final String NO_DESCR = NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.noDescr.text"); for (Map.Entry entry : map.entrySet()) { sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue())); } // add tags property to the sheet - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java index 9b360763a5..f6f3de7680 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalDirectoryNode.java @@ -19,9 +19,12 @@ package org.sleuthkit.autopsy.datamodel; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.LocalDirectory; /** @@ -55,11 +58,15 @@ public class LocalDirectoryNode extends SpecialDirectoryNode { sheet.put(sheetSet); } + List tags = getContentTagsFromDatabase(); sheetSet.put(new NodeProperty<>(Bundle.LocalDirectoryNode_createSheet_name_name(), Bundle.LocalDirectoryNode_createSheet_name_displayName(), Bundle.LocalDirectoryNode_createSheet_name_desc(), getName())); - + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); // At present, a LocalDirectory will never be a datasource - the top level of a logical // file set is a VirtualDirectory Map map = new LinkedHashMap<>(); @@ -69,7 +76,7 @@ public class LocalDirectoryNode extends SpecialDirectoryNode { for (Map.Entry entry : map.entrySet()) { sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue())); } - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index 00c63b745e..a9c09ee1d7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -32,6 +32,7 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; @@ -41,6 +42,7 @@ import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskCoreException; /** @@ -72,7 +74,7 @@ public class LocalFileNode extends AbstractAbstractFileNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + List tags = getContentTagsFromDatabase(); Map map = new LinkedHashMap<>(); fillPropertyMap(map, getContent()); @@ -80,14 +82,17 @@ public class LocalFileNode extends AbstractAbstractFileNode { NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.name.desc"), getName())); - + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); final String NO_DESCR = NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.noDescr.text"); for (Map.Entry entry : map.entrySet()) { sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue())); } // add tags property to the sheet - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java index 837a2ae268..02f382e6ad 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java @@ -21,13 +21,16 @@ package org.sleuthkit.autopsy.datamodel; import java.sql.ResultSet; import java.sql.SQLException; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.logging.Level; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.VirtualDirectory; @@ -79,14 +82,18 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + List tags = getContentTagsFromDatabase(); + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.name"), NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.desc"), getName())); - if (!this.content.isDataSource()) { + CorrelationAttributeInstance correlationAttribute = getCorrelationAttributeInstance(); + addScoreProperty(sheetSet, tags); + addCommentProperty(sheetSet, tags, correlationAttribute); + addCountProperty(sheetSet, correlationAttribute); Map map = new LinkedHashMap<>(); fillPropertyMap(map, getContent()); @@ -94,7 +101,7 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode { for (Map.Entry entry : map.entrySet()) { sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue())); } - addTagProperty(sheetSet); + addTagProperty(sheetSet, tags); } else { sheetSet.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_type_name(), Bundle.VirtualDirectoryNode_createSheet_type_displayName(), diff --git a/Core/src/org/sleuthkit/autopsy/images/notepad16.png b/Core/src/org/sleuthkit/autopsy/images/notepad16.png new file mode 100644 index 0000000000..9f6db6d7a5 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/notepad16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/red-circle-exclamation.png b/Core/src/org/sleuthkit/autopsy/images/red-circle-exclamation.png new file mode 100644 index 0000000000..26b61e6f06 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/red-circle-exclamation.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/yellow-circle-yield.png b/Core/src/org/sleuthkit/autopsy/images/yellow-circle-yield.png new file mode 100644 index 0000000000..85c873f33f Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/yellow-circle-yield.png differ diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties index 5b8d772f9d..e81e5d2fc0 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties @@ -86,6 +86,7 @@ HashDbImportDatabaseDialog.failedToGetDbPathMsg=Failed to get the path of the se HashDbImportDatabaseDialog.importHashDbErr=Import Hash Set Error HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected. HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist. +HashDbImportDatabaseDialog.unableToCopyToUserDirMsg=Unable to copy the hash set to user configuration directory {0}. HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}. HashLookupModuleFactory.moduleName.text=Hash Lookup HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set. @@ -237,3 +238,5 @@ HashDbCreateDatabaseDialog.lbOrg.text=Source Organization: HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path: AddHashValuesToDatabaseDialog.okButton.text_2=OK +HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=Copy hash set into user configuration folder +HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java index a20d0bf5a7..fd4978ef09 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java @@ -60,6 +60,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { private final static String LAST_FILE_PATH_KEY = "HashDbCreate_Path"; private EamOrganization selectedOrg = null; private List orgs = null; + static final String HASH_DATABASE_DIR_NAME = "HashDatabases"; /** * Displays a dialog that allows a user to create a new hash database and @@ -404,7 +405,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed try { - String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString(); + String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), HASH_DATABASE_DIR_NAME).toString(); if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) { lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form index fe5cedc889..e285e99a12 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.form @@ -29,7 +29,7 @@ - + @@ -86,6 +86,7 @@ + @@ -145,7 +146,9 @@ - + + + @@ -354,5 +357,15 @@
+ + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java index d779629846..db27f27365 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java @@ -27,6 +27,7 @@ import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; @@ -180,6 +181,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { fileTypeRadioButton = new javax.swing.JRadioButton(); centralRepoRadioButton = new javax.swing.JRadioButton(); jLabel4 = new javax.swing.JLabel(); + saveInUserConfigFolderCheckbox = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -286,6 +288,9 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.jLabel4.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(saveInUserConfigFolderCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text")); // NOI18N + saveInUserConfigFolderCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -334,6 +339,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addComponent(cancelButton)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(saveInUserConfigFolderCheckbox) .addComponent(jLabel2) .addComponent(readOnlyCheckbox) .addGroup(layout.createSequentialGroup() @@ -384,7 +390,9 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addComponent(readOnlyCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(sendIngestMessagesCheckbox) - .addGap(0, 39, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(saveInUserConfigFolderCheckbox) + .addGap(0, 29, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -397,7 +405,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { }// //GEN-END:initComponents private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed - String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString(); + String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), HashDbCreateDatabaseDialog.HASH_DATABASE_DIR_NAME).toString(); if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) { lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY); } @@ -492,6 +500,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { JOptionPane.ERROR_MESSAGE); return; } + File file = new File(selectedFilePath); if (!file.exists()) { JOptionPane.showMessageDialog(this, @@ -503,6 +512,22 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { return; } + if (saveInUserConfigFolderCheckbox.isSelected()) { + // copy the hash database to user configuration directory and use that path instead (JIRA-4177) + String locationInUserConfigDir = Paths.get(PlatformUtil.getUserConfigDirectory(), HashDbCreateDatabaseDialog.HASH_DATABASE_DIR_NAME, hashSetNameTextField.getText(), file.getName()).toString(); + try { + FileUtils.copyFile(file, new File(locationInUserConfigDir)); + // update the hash database location + selectedFilePath = locationInUserConfigDir; + } catch (IOException ex) { + String errorMessage = NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.unableToCopyToUserDirMsg", locationInUserConfigDir); + Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); + JOptionPane.showMessageDialog(this, errorMessage, NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.importHashDbErr"), + JOptionPane.ERROR_MESSAGE); + return; + } + } + KnownFilesType type; if (knownRadioButton.isSelected()) { type = KnownFilesType.KNOWN; @@ -622,6 +647,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { private javax.swing.JButton orgButton; private javax.swing.JComboBox orgComboBox; private javax.swing.JCheckBox readOnlyCheckbox; + private javax.swing.JCheckBox saveInUserConfigFolderCheckbox; private javax.swing.JCheckBox sendIngestMessagesCheckbox; private javax.swing.ButtonGroup storageTypeButtonGroup; private javax.swing.JTextField versionTextField; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java index f2baef923b..00003da9e7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java @@ -39,6 +39,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -644,7 +645,7 @@ public class HashDbManager implements PropertyChangeListener { return true; } - private String getValidFilePath(String hashSetName, String configuredPath) { + private String getValidFilePath(String hashSetName, String configuredPath) { // Check the configured path. File database = new File(configuredPath); if (database.exists()) { @@ -1238,8 +1239,8 @@ public class HashDbManager implements PropertyChangeListener { EamGlobalFileInstance fileInstance = new EamGlobalFileInstance(referenceSetID, file.getMd5Hash(), type, comment); EamDb.getInstance().addReferenceInstance(fileInstance,EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)); - } catch (EamDbException ex){ - throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); + } catch (EamDbException | CorrelationAttributeNormalizationException ex){ + throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); //NON-NLS } } } @@ -1264,7 +1265,7 @@ public class HashDbManager implements PropertyChangeListener { } try { globalFileInstances.add(new EamGlobalFileInstance(referenceSetID, hashEntry.getMd5Hash(), type, hashEntry.getComment())); - } catch (EamDbException ex){ + } catch (EamDbException | CorrelationAttributeNormalizationException ex){ throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); } } @@ -1295,7 +1296,7 @@ public class HashDbManager implements PropertyChangeListener { if (null != file.getMd5Hash()) { try{ return EamDb.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID); - } catch (EamDbException ex){ + } catch (EamDbException | CorrelationAttributeNormalizationException ex){ Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash " + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS throw new TskCoreException("Error performing central reposiotry hash lookup", ex); @@ -1327,7 +1328,7 @@ public class HashDbManager implements PropertyChangeListener { // Make a bare-bones HashHitInfo for now result = new HashHitInfo(file.getMd5Hash(), "", ""); } - } catch (EamDbException ex){ + } catch (EamDbException | CorrelationAttributeNormalizationException ex){ Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash " + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS throw new TskCoreException("Error performing central reposiotry hash lookup", ex); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java index 081ff5017c..15407a881a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettings.java @@ -62,6 +62,9 @@ final class HashLookupSettings implements Serializable { private static final String CONFIG_FILE_NAME = "hashsets.xml"; //NON-NLS private static final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); + + private static final String USER_DIR_PLACEHOLDER = "[UserConfigFolder]"; + private static final String CURRENT_USER_DIR = PlatformUtil.getUserConfigDirectory(); private static final long serialVersionUID = 1L; private final List hashDbInfoList; @@ -122,10 +125,17 @@ final class HashLookupSettings implements Serializable { * @throws HashLookupSettingsException If there's a problem importing the * settings */ - private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException { + private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException { try { try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(SERIALIZATION_FILE_PATH))) { HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject(); + + /* NOTE: to support JIRA-4177, we need to check if any of the hash + database paths are in Windows user directory. If so, we replace the path + with USER_DIR_PLACEHOLDER before saving to disk. When reading from disk, + USER_DIR_PLACEHOLDER needs to be replaced with current user directory path. + */ + convertPlaceholderToPath(filesSetsSettings); return filesSetsSettings; } } catch (IOException | ClassNotFoundException ex) { @@ -282,8 +292,16 @@ final class HashLookupSettings implements Serializable { */ static boolean writeSettings(HashLookupSettings settings) { + /* NOTE: to support JIRA-4177, we need to check if any of the hash + database paths are in Windows user directory. If so, replace the path + with USER_DIR_PLACEHOLDER so that when it is read, it gets updated to be + the current user directory path. + */ + convertPathToPlaceholder(settings); try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(SERIALIZATION_FILE_PATH))) { out.writeObject(settings); + // restore the paths, in case they are going to be used somewhere + convertPlaceholderToPath(settings); return true; } catch (Exception ex) { logger.log(Level.SEVERE, "Could not write hash set settings."); @@ -291,13 +309,54 @@ final class HashLookupSettings implements Serializable { } } + /** + * For file type hash sets, check if hash set paths needs to be modified + * per JIRA-4177. If the file path is in current Windows user directory, + * replace the path with USER_DIR_PLACEHOLDER. + * + * @param settings HashLookupSettings settings object to examiner and modify + */ + static void convertPathToPlaceholder(HashLookupSettings settings) { + for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) { + if (hashDbInfo.isFileDatabaseType()) { + String dbPath = hashDbInfo.getPath(); + if (dbPath.startsWith(CURRENT_USER_DIR)) { + // replace the current user directory with place holder + String remainingPath = dbPath.substring(CURRENT_USER_DIR.length()); + hashDbInfo.setPath(USER_DIR_PLACEHOLDER + remainingPath); + } + } + } + } + + /** + * For file type hash sets, check if hash set paths needs to be modified per + * JIRA-4177. Replace USER_DIR_PLACEHOLDER with path to current Windows user + * directory. + * + * @param settings HashLookupSettings settings object to examiner and modify + */ + static void convertPlaceholderToPath(HashLookupSettings settings) { + for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) { + if (hashDbInfo.isFileDatabaseType()) { + String dbPath = hashDbInfo.getPath(); + if (dbPath.startsWith(USER_DIR_PLACEHOLDER)) { + // replace the place holder with current user directory + String remainingPath = dbPath.substring(USER_DIR_PLACEHOLDER.length()); + hashDbInfo.setPath(CURRENT_USER_DIR + remainingPath); + } + } + } + } + + /** * Represents the serializable information within a hash lookup in order to * be written to disk. Used to hand off information when loading and saving * hash lookups. */ static final class HashDbInfo implements Serializable { - + enum DatabaseType{ FILE, CENTRAL_REPOSITORY @@ -308,7 +367,7 @@ final class HashLookupSettings implements Serializable { private final HashDbManager.HashDb.KnownFilesType knownFilesType; private boolean searchDuringIngest; private final boolean sendIngestMessages; - private final String path; + private String path; private final String version; private final boolean readOnly; private final int referenceSetID; @@ -345,7 +404,7 @@ final class HashLookupSettings implements Serializable { this.searchDuringIngest = searchDuringIngest; this.sendIngestMessages = sendIngestMessages; this.path = ""; - dbType = DatabaseType.CENTRAL_REPOSITORY; + dbType = DatabaseType.CENTRAL_REPOSITORY; } HashDbInfo(HashDbManager.HashDb db) throws TskCoreException{ @@ -445,6 +504,14 @@ final class HashLookupSettings implements Serializable { */ String getPath() { return path; + } + + /** + * Sets the path. + * @param path the path to set + */ + public void setPath(String path) { + this.path = path; } int getReferenceSetID(){ diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java index 69384bfe0c..722af5494a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java @@ -1068,13 +1068,17 @@ class TableReportGenerator { orderedRowData.add(makeCommaSeparatedList(getTags())); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == getArtifact().getArtifactTypeID()) { - String[] attributeDataArray = new String[3]; + String[] attributeDataArray = new String[5]; // Array is used so that order of the attributes is maintained. for (BlackboardAttribute attr : attributes) { if (attr.getAttributeType().equals(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME))) { attributeDataArray[0] = attr.getDisplayString(); } else if (attr.getAttributeType().equals(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY))) { attributeDataArray[1] = attr.getDisplayString(); + } else if (attr.getAttributeType().equals(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT))) { + attributeDataArray[3] = attr.getDisplayString(); + } else if (attr.getAttributeType().equals(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION))) { + attributeDataArray[4] = attr.getDisplayString(); } } @@ -1163,6 +1167,7 @@ class TableReportGenerator { * * @return List row titles */ + @Messages({"ReportGenerator.artTableColHdr.comment=Comment"}) private List getArtifactTableColumns(int artifactTypeId, Set attributeTypeSet) { ArrayList columns = new ArrayList<>(); @@ -1557,6 +1562,12 @@ class TableReportGenerator { columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.tskPath"), new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH))); + + columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.comment"), + new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT))); + + columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.description"), + new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION))); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID() == artifactTypeId) { columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.tskGpsRouteCategory"), diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java index 543eb59de5..db3c6d18ec 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java @@ -29,7 +29,10 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Iterator; +import java.util.Random; import java.util.stream.Collectors; +import java.util.stream.IntStream; import junit.framework.Test; import junit.framework.TestCase; import org.apache.commons.io.FileUtils; @@ -164,7 +167,6 @@ public class CentralRepoDatamodelTest extends TestCase { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } - } @Override @@ -233,7 +235,7 @@ public class CentralRepoDatamodelTest extends TestCase { for (CorrelationAttributeInstance a : attrs) { assertTrue("Artifact did not have expected BAD status", a.getKnownStatus().equals(TskData.FileKnown.BAD)); } - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -258,10 +260,10 @@ public class CentralRepoDatamodelTest extends TestCase { } else if (case2.getCaseUUID().equals(a.getCorrelationCase().getCaseUUID())) { assertTrue("Artifact did not have expected KNOWN status", a.getKnownStatus().equals(TskData.FileKnown.KNOWN)); } else { - Assert.fail("getArtifactInstancesByTypeValue returned unexpected case"); + fail("getArtifactInstancesByTypeValue returned unexpected case"); } } - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -278,71 +280,79 @@ public class CentralRepoDatamodelTest extends TestCase { List attrs = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, hashToChangeToNotable); assertTrue("getArtifactInstancesByTypeValue returned " + attrs.size() + " values - expected 1", attrs.size() == 1); assertTrue("Artifact status did not change to BAD", attrs.get(0).getKnownStatus().equals(TskData.FileKnown.BAD)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Try to update artifact with two CorrelationAttributeInstance instances - // This appears to no longer be a valid test, already moved fail to the catch since it no longe fails. try { - CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase1, BAD_PATH, "", TskData.FileKnown.KNOWN); - CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase2, "badPath", + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase2, BAD_PATH, "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr1, TskData.FileKnown.BAD); EamDb.getInstance().setAttributeInstanceKnownStatus(attr2, TskData.FileKnown.BAD); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Assert.fail("setArtifactInstanceKnownStatus threw an exception for sequential Correlation Attribute Instances updates"); } // Try to update null artifact try { EamDb.getInstance().setAttributeInstanceKnownStatus(null, TskData.FileKnown.BAD); - Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null correlation attribute"); + fail("setArtifactInstanceKnownStatus failed to throw exception for null correlation attribute"); } catch (EamDbException ex) { // This is the expected behavior } // Try to update artifact with null known status try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase1, BAD_PATH, "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, null); - Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null known status"); + fail("setArtifactInstanceKnownStatus failed to throw exception for null known status"); } catch (EamDbException ex) { // This is the expected behavior + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Try to update artifact with null case try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, null, dataSource1fromCase1, "badPath", + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, null, dataSource1fromCase1, BAD_PATH, "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); - Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); + fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); } catch (EamDbException ex) { // This is the expected behavior + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Try to update artifact with null data source try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, case1, null, "badPath", + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, case1, null, BAD_PATH, "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); - Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); + fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); } catch (EamDbException ex) { // This is the expected behavior + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test getting two notable instances try { List attrs = EamDb.getInstance().getArtifactInstancesKnownBad(fileType, notableHashInBothCases); assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected 2", attrs.size() == 2); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -351,7 +361,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { List attrs = EamDb.getInstance().getArtifactInstancesKnownBad(fileType, notableHashInOneCaseKnownOther); assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected 1", attrs.size() == 1); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -359,15 +369,22 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting notable instances with null type try { EamDb.getInstance().getArtifactInstancesKnownBad(null, notableHashInOneCaseKnownOther); - Assert.fail("getArtifactInstancesKnownBad failed to throw exception for null type"); - } catch (EamDbException ex) { + fail("getArtifactInstancesKnownBad failed to throw exception for null type"); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail("should have got CentralRepoValidationException"); } - // Test getting notable instances with null value (should work fine) + // Test getting notable instances with null value try { - List attrs = EamDb.getInstance().getArtifactInstancesKnownBad(fileType, null); - assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected ", attrs.isEmpty()); + EamDb.getInstance().getArtifactInstancesKnownBad(fileType, null); + fail("should get an exception for null inout"); + } catch (CorrelationAttributeNormalizationException ex) { + //this is expecpted + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); @@ -377,7 +394,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { long count = EamDb.getInstance().getCountArtifactInstancesKnownBad(fileType, notableHashInBothCases); assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected 2", count == 2); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -386,7 +403,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { long count = EamDb.getInstance().getCountArtifactInstancesKnownBad(fileType, notableHashInOneCaseKnownOther); assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected 1", count == 1); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -394,25 +411,31 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting notable instance count with null type try { EamDb.getInstance().getCountArtifactInstancesKnownBad(null, notableHashInOneCaseKnownOther); - Assert.fail("getCountArtifactInstancesKnownBad failed to throw exception for null type"); - } catch (EamDbException ex) { - // This is the expected behavior - } - - // Test getting notable instance count with null value (should work fine) - try { - long count = EamDb.getInstance().getCountArtifactInstancesKnownBad(fileType, null); - assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected ", count == 0); + fail("getCountArtifactInstancesKnownBad failed to throw exception for null type"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ + // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } + + // Test getting notable instance count with null value (should throw an exception) + try { + EamDb.getInstance().getCountArtifactInstancesKnownBad(fileType, null); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ + // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting cases with notable instances (all instances are notable) try { List cases = EamDb.getInstance().getListCasesHavingArtifactInstancesKnownBad(fileType, notableHashInBothCases); assertTrue("getListCasesHavingArtifactInstancesKnownBad returned " + cases.size() + " values - expected 2", cases.size() == 2); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -422,7 +445,7 @@ public class CentralRepoDatamodelTest extends TestCase { List cases = EamDb.getInstance().getListCasesHavingArtifactInstancesKnownBad(fileType, notableHashInOneCaseKnownOther); assertTrue("getListCasesHavingArtifactInstancesKnownBad returned " + cases.size() + " values - expected 1", cases.size() == 1); assertTrue("getListCasesHavingArtifactInstancesKnownBad returned unexpected case " + cases.get(0), case1.getDisplayName().equals(cases.get(0))); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -430,20 +453,28 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting cases with null type try { EamDb.getInstance().getListCasesHavingArtifactInstancesKnownBad(null, notableHashInOneCaseKnownOther); - Assert.fail("getListCasesHavingArtifactInstancesKnownBad failed to throw exception for null type"); + fail("getListCasesHavingArtifactInstancesKnownBad failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } - // Test getting cases with null value (should work fine) + // Test getting cases with null value (should throw exception) try { List cases = EamDb.getInstance().getListCasesHavingArtifactInstancesKnownBad(fileType, null); assertTrue("getListCasesHavingArtifactInstancesKnownBad returned " + cases.size() + " values - expected ", cases.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ + // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } + private static final String BAD_PATH = "badPath"; /** * Test the methods associated with bulk artifacts (addAttributeInstanceBulk and @@ -476,7 +507,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Create the first list, which will have (bulkThreshold / 2) entries List list1 = new ArrayList<>(); for (int i = 0; i < DEFAULT_BULK_THRESHOLD / 2; i++) { - String value = "bulkInsertValue1_" + String.valueOf(i); + String value = randomHash(); String path = "C:\\bulkInsertPath1\\file" + String.valueOf(i); CorrelationAttributeInstance attr = new CorrelationAttributeInstance(value, fileType, case1, dataSource1fromCase1, path); @@ -495,7 +526,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Make a second list with length equal to bulkThreshold List list2 = new ArrayList<>(); for (int i = 0; i < DEFAULT_BULK_THRESHOLD; i++) { - String value = "bulkInsertValue2_" + String.valueOf(i); + String value = randomHash(); String path = "C:\\bulkInsertPath2\\file" + String.valueOf(i); CorrelationAttributeInstance attr = new CorrelationAttributeInstance(value, fileType, case1, dataSource1fromCase1, path); @@ -517,57 +548,77 @@ public class CentralRepoDatamodelTest extends TestCase { int expectedCount = list1.size() + list2.size(); assertTrue("Artifact count " + count + " does not match expected count " + expectedCount, count == expectedCount); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test preparing artifact with null type try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "value"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, randomHash()); EamDb.getInstance().addAttributeInstanceBulk(attr); - Assert.fail("prepareBulkArtifact failed to throw exception for null type"); + fail("prepareBulkArtifact failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test preparing artifact with null case try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, null, dataSource1fromCase1, "path"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, null, dataSource1fromCase1, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttributeInstancesBulk(); - Assert.fail("bulkInsertArtifacts failed to throw exception for null case"); + fail("bulkInsertArtifacts failed to throw exception for null case"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test preparing artifact with null data source try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, null, "path"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, case1, null, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttributeInstancesBulk(); - Assert.fail("prepareBulkArtifact failed to throw exception for null data source"); + fail("prepareBulkArtifact failed to throw exception for null data source"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test preparing artifact with null path // CorrelationAttributeInstance will throw an exception try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, null); - Assert.fail("CorrelationAttributeInstance failed to throw exception for null path"); + new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase1, null); + fail("CorrelationAttributeInstance failed to throw exception for null path"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test preparing artifact with null known status try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, "path", "comment", null); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase1, "path", "comment", null); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttributeInstancesBulk(); - Assert.fail("prepareBulkArtifact failed to throw exception for null known status"); + fail("prepareBulkArtifact failed to throw exception for null known status"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } } @@ -659,7 +710,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(onlyInDataSource3Hash, fileType, case2, dataSource1fromCase2, onlyInDataSource3Path); EamDb.getInstance().addArtifactInstance(attr); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -673,7 +724,7 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationAttributeInstance attr3 = new CorrelationAttributeInstance(inAllDataSourcesHash, fileType, case2, dataSource1fromCase2, inAllDataSourcesPath); EamDb.getInstance().addArtifactInstance(attr3); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -685,7 +736,7 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(inDataSource1twiceHash, fileType, case1, dataSource1fromCase1, inDataSource1twicePath2); EamDb.getInstance().addArtifactInstance(attr2); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -695,7 +746,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(emailValue, emailType, case1, dataSource1fromCase1, emailPath); EamDb.getInstance().addArtifactInstance(attr); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -708,7 +759,7 @@ public class CentralRepoDatamodelTest extends TestCase { case1, dataSource1fromCase1, phonePath); EamDb.getInstance().addArtifactInstance(attr); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -720,7 +771,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID), case1, dataSource1fromCase1, domainPath); EamDb.getInstance().addArtifactInstance(attr); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -733,95 +784,117 @@ public class CentralRepoDatamodelTest extends TestCase { case1, dataSource1fromCase1, devIdPath); EamDb.getInstance().addArtifactInstance(attr); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } - // Test CorrelationAttributeInstance failure cases - // Create an attribute to use in the next few tests - CorrelationAttributeInstance failAttr; + // Test CorrelationAttributeInstance creation try { - failAttr = new CorrelationAttributeInstance(fileType, "badInstances"); - } catch (EamDbException ex) { + new CorrelationAttributeInstance(fileType, randomHash()); + } catch (CorrelationAttributeNormalizationException | EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); - return; } // Test adding instance with null case try { - CorrelationAttributeInstance failAttrInst = new CorrelationAttributeInstance("badInstances", fileType, null, dataSource1fromCase2, "badPath"); + CorrelationAttributeInstance failAttrInst = new CorrelationAttributeInstance("badInstances", fileType, null, dataSource1fromCase2, BAD_PATH); EamDb.getInstance().addArtifactInstance(failAttrInst); - Assert.fail("addArtifact failed to throw exception for null case"); + fail("addArtifact failed to throw exception for null case"); } catch (EamDbException ex) { + fail("was expecting to get CorrelationAttributeNormalizationException"); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test adding instance with invalid case ID try { CorrelationCase badCase = new CorrelationCase("badCaseUuid", "badCaseName"); - CorrelationAttributeInstance failAttrInst2 = new CorrelationAttributeInstance("badInstances", fileType, badCase, dataSource1fromCase2, "badPath"); + CorrelationAttributeInstance failAttrInst2 = new CorrelationAttributeInstance(randomHash(), fileType, badCase, dataSource1fromCase2, BAD_PATH); EamDb.getInstance().addArtifactInstance(failAttrInst2); - Assert.fail("addArtifact failed to throw exception for invalid case"); + fail("addArtifact failed to throw exception for invalid case"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + fail("was expecting to get EamDbException"); } // Test adding instance with null data source try { - CorrelationAttributeInstance failAttrInst3 = new CorrelationAttributeInstance("badInstances", fileType, case1, null, "badPath"); + CorrelationAttributeInstance failAttrInst3 = new CorrelationAttributeInstance(randomHash(), fileType, case1, null, BAD_PATH); EamDb.getInstance().addArtifactInstance(failAttrInst3); - Assert.fail("addArtifact failed to throw exception for null data source"); + fail("addArtifact failed to throw exception for null data source"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + fail("was expecting to get EamDbException"); } // Test adding instance with invalid data source ID try { CorrelationDataSource badDS = new CorrelationDataSource(case1, "badDSUuid", "badDSName"); - CorrelationAttributeInstance failAttrInst4 = new CorrelationAttributeInstance("badInstances", fileType, case1, badDS, "badPath"); + CorrelationAttributeInstance failAttrInst4 = new CorrelationAttributeInstance(randomHash(), fileType, case1, badDS, BAD_PATH); EamDb.getInstance().addArtifactInstance(failAttrInst4); - Assert.fail("addArtifact failed to throw exception for invalid data source"); + fail("addArtifact failed to throw exception for invalid data source"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + fail("was expecting to get EamDbException"); } // Test adding instance with null path // This will fail in the CorrelationAttributeInstance constructor try { - new CorrelationAttributeInstance("badInstances", fileType, case1, dataSource1fromCase1, null); - Assert.fail("CorrelationAttributeInstance failed to throw exception for null path"); + new CorrelationAttributeInstance(randomHash(), fileType, case1, dataSource1fromCase1, null); + fail("CorrelationAttributeInstance failed to throw exception for null path"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + fail("was expecting to get EamDbException"); } // Test adding instance with null known status try { CorrelationAttributeInstance failAttrInst5 = new CorrelationAttributeInstance("badInstances", fileType, case1, dataSource1fromCase1, null, "comment", null); EamDb.getInstance().addArtifactInstance(failAttrInst5); - Assert.fail("addArtifact failed to throw exception for null known status"); + fail("addArtifact failed to throw exception for null known status"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + fail("was expecting to get EamDbException"); } // Test CorrelationAttribute failure cases // Test null type try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "badInstances"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, randomHash()); EamDb.getInstance().addArtifactInstance(attr); - Assert.fail("addArtifact failed to throw exception for null type"); + fail("addArtifact failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test null value // This will fail in the CorrelationAttribute constructor try { new CorrelationAttributeInstance(fileType, null); - Assert.fail("addArtifactInsance failed to throw exception for null value"); - } catch (EamDbException ex) { + fail("addArtifact failed to throw exception for null value"); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (EamDbException ex) { + fail("expected to get CorrelationAttributeNormalizationException"); } // Test getting instances with expected results @@ -834,37 +907,45 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesByTypeValue returned instance with unexpected path " + inst.getFilePath(), inAllDataSourcesPath.equalsIgnoreCase(inst.getFilePath())); } - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } - // Test getting instances expecting no results + // Test getting instances with mismatched data / data-type and expect an exception try { - List instances = EamDb.getInstance().getArtifactInstancesByTypeValue( - emailType, inAllDataSourcesHash); - assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 0", instances.isEmpty()); + EamDb.getInstance().getArtifactInstancesByTypeValue(emailType, inAllDataSourcesHash); + fail("we should get an exception"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instances with null type try { EamDb.getInstance().getArtifactInstancesByTypeValue(null, inAllDataSourcesHash); - Assert.fail("getArtifactInstancesByTypeValue failed to throw exception for null type"); + fail("getArtifactInstancesByTypeValue failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instances with null value - // Should just return nothing try { - List instances = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, null); - assertTrue("getArtifactInstancesByTypeValue returned non-empty list for null value", instances.isEmpty()); + EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, null); + fail("this should produce an exception"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch(CorrelationAttributeNormalizationException ex){ + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instances with path that should produce results @@ -888,33 +969,35 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting instances with null type try { EamDb.getInstance().getArtifactInstancesByPath(null, inAllDataSourcesPath); - Assert.fail("getArtifactInstancesByPath failed to throw exception for null type"); + fail("getArtifactInstancesByPath failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instances with null path try { EamDb.getInstance().getArtifactInstancesByPath(fileType, null); - Assert.fail("getArtifactInstancesByPath failed to throw exception for null path"); + fail("getArtifactInstancesByPath failed to throw exception for null path"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instance count with path that should produce results try { long count = EamDb.getInstance().getCountArtifactInstancesByTypeValue(fileType, inAllDataSourcesHash); assertTrue("getCountArtifactInstancesByTypeValue returned " + count + " - expected 3", count == 3); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test getting instance count with path that should not produce results try { - long count = EamDb.getInstance().getCountArtifactInstancesByTypeValue(fileType, "xyz"); + long count = EamDb.getInstance().getCountArtifactInstancesByTypeValue(fileType, randomHash()); assertTrue("getCountArtifactInstancesByTypeValue returned " + count + " - expected 0", count == 0); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -922,17 +1005,25 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting instance count with null type try { EamDb.getInstance().getCountArtifactInstancesByTypeValue(null, inAllDataSourcesHash); - Assert.fail("getCountArtifactInstancesByTypeValue failed to throw exception for null type"); + fail("getCountArtifactInstancesByTypeValue failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch(CorrelationAttributeNormalizationException ex){ // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting instance count with null value try { EamDb.getInstance().getCountArtifactInstancesByTypeValue(fileType, null); - Assert.fail("getCountArtifactInstancesByTypeValue failed to throw exception for null value"); + fail("getCountArtifactInstancesByTypeValue failed to throw exception for null value"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch(CorrelationAttributeNormalizationException ex){ // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting frequency of value that is in all three data sources @@ -940,7 +1031,7 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, inAllDataSourcesHash); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 100", freq == 100); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -950,7 +1041,7 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, inDataSource1twiceHash); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -960,17 +1051,17 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(emailType, emailValue); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test getting frequency of non-existent value try { - CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, "randomValue"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, randomHash()); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 0", freq == 0); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -979,17 +1070,22 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "randomValue"); EamDb.getInstance().getFrequencyPercentage(attr); - Assert.fail("getFrequencyPercentage failed to throw exception for null type"); - } catch (EamDbException ex) { + fail("getFrequencyPercentage failed to throw exception for null type"); + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting frequency with null attribute try { EamDb.getInstance().getFrequencyPercentage(null); - Assert.fail("getFrequencyPercentage failed to throw exception for null attribute"); + fail("getFrequencyPercentage failed to throw exception for null attribute"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test updating a correlation attribute instance comment @@ -1006,7 +1102,7 @@ public class CentralRepoDatamodelTest extends TestCase { usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); assertEquals("updateAttributeInstanceComment did not set comment to \"new comment\".", "new comment", correlationAttribute.getComment()); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1042,7 +1138,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { long count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, inAllDataSourcesHash); assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 3", count == 3); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1051,35 +1147,42 @@ public class CentralRepoDatamodelTest extends TestCase { try { long count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, inDataSource1twiceHash); assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 1", count == 1); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test getting data source count for entry that is not in any data sources try { - long count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, "abcdef"); + long count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, randomHash()); assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 0", count == 0); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test getting data source count for null type try { - EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(null, "abcdef"); - Assert.fail("getCountUniqueCaseDataSourceTuplesHavingTypeValue failed to throw exception for null type"); + EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(null, randomHash()); + fail("getCountUniqueCaseDataSourceTuplesHavingTypeValue failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting data source count for null value try { - long count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, null); - assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 0", count == 0); + EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(fileType, null); + fail("we should get an exception here"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test running processinstance which queries all rows from instances table @@ -1096,16 +1199,17 @@ public class CentralRepoDatamodelTest extends TestCase { int count2 = instancetableCallback.getCounterNamingConvention(); assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); assertTrue("Process Instance count with filepath without naming convention: " + count1 + "-expected greater than 0", count1 > 0); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); } try { //test null inputs EamDb.getInstance().processInstanceTable(null, null); - Assert.fail("processinstance method failed to throw exception for null type value"); + fail("processinstance method failed to throw exception for null type value"); } catch (EamDbException ex) { - // This is the expected + // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test running processinstance which queries all rows from instances table @@ -1122,16 +1226,17 @@ public class CentralRepoDatamodelTest extends TestCase { int count2 = instancetableCallback.getCounterNamingConvention(); assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); assertTrue("Process Instance count with filepath without naming convention: " + count1 + "-expected greater than 0", count1 > 0); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); } try { //test null inputs EamDb.getInstance().processInstanceTableWhere(null, null, null); - Assert.fail("processinstance method failed to throw exception for null type value"); + fail("processinstance method failed to throw exception for null type value"); } catch (EamDbException ex) { - // This is the expected + // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } @@ -1178,18 +1283,20 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance.Type temp = new CorrelationAttributeInstance.Type(customTypeName, customTypeDb, false, false); EamDb.getInstance().newCorrelationType(temp); - Assert.fail("newCorrelationType failed to throw exception for duplicate name/db table"); + fail("newCorrelationType failed to throw exception for duplicate name/db table"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test new type with null name try { CorrelationAttributeInstance.Type temp = new CorrelationAttributeInstance.Type(null, "temp_type", false, false); EamDb.getInstance().newCorrelationType(temp); - Assert.fail("newCorrelationType failed to throw exception for null name table"); + fail("newCorrelationType failed to throw exception for null name table"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test new type with null db name @@ -1199,14 +1306,16 @@ public class CentralRepoDatamodelTest extends TestCase { Assert.fail("CorrelationAttributeInstance.Type failed to throw exception for null db table name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test new type with null type try { EamDb.getInstance().newCorrelationType(null); - Assert.fail("newCorrelationType failed to throw exception for null type"); + fail("newCorrelationType failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting all correlation types @@ -1255,9 +1364,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting the type with a invalid ID try { EamDb.getInstance().getCorrelationTypeById(5555); - Assert.fail("getCorrelationTypeById failed to throw exception for invalid ID"); + fail("getCorrelationTypeById failed to throw exception for invalid ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test updating a valid type @@ -1298,18 +1408,20 @@ public class CentralRepoDatamodelTest extends TestCase { try { customType.setDisplayName(null); EamDb.getInstance().updateCorrelationType(customType); - Assert.fail("updateCorrelationType failed to throw exception for null name"); + fail("updateCorrelationType failed to throw exception for null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test updating a null type try { customType.setDisplayName(null); EamDb.getInstance().updateCorrelationType(customType); - Assert.fail("updateCorrelationType failed to throw exception for null type"); + fail("updateCorrelationType failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } @@ -1373,26 +1485,29 @@ public class CentralRepoDatamodelTest extends TestCase { try { EamOrganization temp = new EamOrganization(orgAname); EamDb.getInstance().newOrganization(temp); - Assert.fail("newOrganization failed to throw exception for duplicate org name"); + fail("newOrganization failed to throw exception for duplicate org name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test adding null organization try { EamDb.getInstance().newOrganization(null); - Assert.fail("newOrganization failed to throw exception for null org"); + fail("newOrganization failed to throw exception for null org"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test adding organization with null name try { EamOrganization temp = new EamOrganization(null); EamDb.getInstance().newOrganization(temp); - Assert.fail("newOrganization failed to throw exception for null name"); + fail("newOrganization failed to throw exception for null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting organizations @@ -1422,9 +1537,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting org with invalid ID try { EamDb.getInstance().getOrganizationByID(12345); - Assert.fail("getOrganizationByID failed to throw exception for invalid ID"); + fail("getOrganizationByID failed to throw exception for invalid ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test updating valid org @@ -1457,17 +1573,19 @@ public class CentralRepoDatamodelTest extends TestCase { try { EamOrganization temp = new EamOrganization("invalidOrg"); EamDb.getInstance().updateOrganization(temp); - Assert.fail("updateOrganization worked for invalid ID"); + fail("updateOrganization worked for invalid ID"); } catch (EamDbException ex) { // this is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test updating null org try { EamDb.getInstance().updateOrganization(null); - Assert.fail("updateOrganization failed to throw exception for null org"); + fail("updateOrganization failed to throw exception for null org"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test updating org to null name @@ -1475,9 +1593,10 @@ public class CentralRepoDatamodelTest extends TestCase { EamOrganization copyOfA = EamDb.getInstance().getOrganizationByID(orgA.getOrgID()); copyOfA.setName(null); EamDb.getInstance().updateOrganization(copyOfA); - Assert.fail("updateOrganization failed to throw exception for null name"); + fail("updateOrganization failed to throw exception for null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test deleting existing org that isn't in use @@ -1505,26 +1624,29 @@ public class CentralRepoDatamodelTest extends TestCase { // It should now throw an exception if we try to delete it EamDb.getInstance().deleteOrganization(inUseOrg); - Assert.fail("deleteOrganization failed to throw exception for in use organization"); + fail("deleteOrganization failed to throw exception for in use organization"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test deleting non-existent org try { EamOrganization temp = new EamOrganization("temp"); EamDb.getInstance().deleteOrganization(temp); - Assert.fail("deleteOrganization failed to throw exception for non-existent organization"); + fail("deleteOrganization failed to throw exception for non-existent organization"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test deleting null org try { EamDb.getInstance().deleteOrganization(null); - Assert.fail("deleteOrganization failed to throw exception for null organization"); + fail("deleteOrganization failed to throw exception for null organization"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } @@ -1626,7 +1748,7 @@ public class CentralRepoDatamodelTest extends TestCase { temp = new EamGlobalFileInstance(knownSet1id, knownHash1, TskData.FileKnown.KNOWN, "comment5"); EamDb.getInstance().addReferenceInstance(temp, fileType); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1635,9 +1757,13 @@ public class CentralRepoDatamodelTest extends TestCase { try { EamGlobalFileInstance temp = new EamGlobalFileInstance(2345, inAllSetsHash, TskData.FileKnown.BAD, "comment"); EamDb.getInstance().addReferenceInstance(temp, fileType); - Assert.fail("addReferenceInstance failed to throw exception for invalid ID"); + fail("addReferenceInstance failed to throw exception for invalid ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test creating file instance with null hash @@ -1645,9 +1771,13 @@ public class CentralRepoDatamodelTest extends TestCase { // call addReferenceInstance and just test the EamGlobalFileInstance constructor try { new EamGlobalFileInstance(notableSet1id, null, TskData.FileKnown.BAD, "comment"); - Assert.fail("EamGlobalFileInstance failed to throw exception for null hash"); + fail("EamGlobalFileInstance failed to throw exception for null hash"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test adding file instance with null known status @@ -1655,27 +1785,34 @@ public class CentralRepoDatamodelTest extends TestCase { // call addReferenceInstance and just test the EamGlobalFileInstance constructor try { new EamGlobalFileInstance(notableSet1id, inAllSetsHash, null, "comment"); - Assert.fail("EamGlobalFileInstance failed to throw exception for null type"); + fail("EamGlobalFileInstance failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test adding file instance with null correlation type try { EamGlobalFileInstance temp = new EamGlobalFileInstance(notableSet1id, inAllSetsHash, TskData.FileKnown.BAD, "comment"); EamDb.getInstance().addReferenceInstance(temp, null); - Assert.fail("addReferenceInstance failed to throw exception for null type"); + fail("addReferenceInstance failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test bulk insert with large valid set try { // Create a list of global file instances. Make enough that the bulk threshold should be hit once. Set instances = new HashSet<>(); - String bulkTestHash = "bulktesthash_"; for (int i = 0; i < DEFAULT_BULK_THRESHOLD * 1.5; i++) { - String hash = bulkTestHash + String.valueOf(i); + String hash = randomHash(); instances.add(new EamGlobalFileInstance(notableSet2id, hash, TskData.FileKnown.BAD, null)); } @@ -1684,10 +1821,10 @@ public class CentralRepoDatamodelTest extends TestCase { // There's no way to get a count of the number of entries in the database, so just do a spot check if (DEFAULT_BULK_THRESHOLD > 10) { - String hash = bulkTestHash + "10"; + String hash = instances.stream().findFirst().get().getMD5Hash(); assertTrue("Sample bulk insert instance not found", EamDb.getInstance().isFileHashInReferenceSet(hash, notableSet2id)); } - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1695,43 +1832,51 @@ public class CentralRepoDatamodelTest extends TestCase { // Test bulk add file instance with null list try { EamDb.getInstance().bulkInsertReferenceTypeEntries(null, fileType); - Assert.fail("bulkInsertReferenceTypeEntries failed to throw exception for null list"); + fail("bulkInsertReferenceTypeEntries failed to throw exception for null list"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test bulk add file instance with invalid reference set ID try { Set tempSet = new HashSet<>(Arrays.asList(new EamGlobalFileInstance(2345, inAllSetsHash, TskData.FileKnown.BAD, "comment"))); EamDb.getInstance().bulkInsertReferenceTypeEntries(tempSet, fileType); - Assert.fail("bulkInsertReferenceTypeEntries failed to throw exception for invalid ID"); + fail("bulkInsertReferenceTypeEntries failed to throw exception for invalid ID"); } catch (EamDbException ex) { // This is the expected behavior + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test bulk add file instance with null correlation type try { Set tempSet = new HashSet<>(Arrays.asList(new EamGlobalFileInstance(notableSet1id, inAllSetsHash, TskData.FileKnown.BAD, "comment"))); EamDb.getInstance().bulkInsertReferenceTypeEntries(tempSet, null); - Assert.fail("bulkInsertReferenceTypeEntries failed to throw exception for null type"); + fail("bulkInsertReferenceTypeEntries failed to throw exception for null type"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); } // Test getting reference instances with valid data try { List temp = EamDb.getInstance().getReferenceInstancesByTypeValue(fileType, inAllSetsHash); assertTrue("getReferenceInstancesByTypeValue returned " + temp.size() + " instances - expected 3", temp.size() == 3); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test getting reference instances with non-existent data try { - List temp = EamDb.getInstance().getReferenceInstancesByTypeValue(fileType, "testHash"); + List temp = EamDb.getInstance().getReferenceInstancesByTypeValue(fileType, randomHash()); assertTrue("getReferenceInstancesByTypeValue returned " + temp.size() + " instances for non-existent value - expected 0", temp.isEmpty()); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1739,32 +1884,40 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting reference instances an invalid type (the email table is not yet implemented) try { EamDb.getInstance().getReferenceInstancesByTypeValue(emailType, inAllSetsHash); - Assert.fail("getReferenceInstancesByTypeValue failed to throw exception for invalid table"); - } catch (EamDbException ex) { + fail("getReferenceInstancesByTypeValue failed to throw exception for invalid table"); + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting reference instances with null type try { EamDb.getInstance().getReferenceInstancesByTypeValue(null, inAllSetsHash); - Assert.fail("getReferenceInstancesByTypeValue failed to throw exception for null type"); + fail("getReferenceInstancesByTypeValue failed to throw exception for null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting reference instances with null value try { List temp = EamDb.getInstance().getReferenceInstancesByTypeValue(fileType, null); - assertTrue("getReferenceInstancesByTypeValue returned non-empty list given null value", temp.isEmpty()); + fail("we should get an exception here"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch(CorrelationAttributeNormalizationException ex){ + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test checking existing hash/ID try { assertTrue("isFileHashInReferenceSet returned false for valid data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, knownSet1id)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1772,7 +1925,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test checking non-existent (but valid) hash/ID try { assertFalse("isFileHashInReferenceSet returned true for non-existent data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, notableSet1id)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1780,24 +1933,28 @@ public class CentralRepoDatamodelTest extends TestCase { // Test checking invalid reference set ID try { assertFalse("isFileHashInReferenceSet returned true for invalid data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, 5678)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test checking null hash try { - assertFalse("isFileHashInReferenceSet returned true for null hash", EamDb.getInstance().isFileHashInReferenceSet(null, knownSet1id)); + EamDb.getInstance().isFileHashInReferenceSet(null, knownSet1id); + fail("This should throw an exception"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch(CorrelationAttributeNormalizationException ex){ + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test checking existing hash/ID try { assertTrue("isValueInReferenceSet returned false for valid data", EamDb.getInstance().isValueInReferenceSet(knownHash1, knownSet1id, fileType.getId())); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1806,7 +1963,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { assertFalse("isValueInReferenceSet returned true for non-existent data", EamDb.getInstance().isValueInReferenceSet(knownHash1, notableSet1id, fileType.getId())); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1815,33 +1972,40 @@ public class CentralRepoDatamodelTest extends TestCase { try { assertFalse("isValueInReferenceSet returned true for invalid data", EamDb.getInstance().isValueInReferenceSet(knownHash1, 5678, fileType.getId())); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test checking null hash try { - assertFalse("isValueInReferenceSet returned true for null value", - EamDb.getInstance().isValueInReferenceSet(null, knownSet1id, fileType.getId())); + EamDb.getInstance().isValueInReferenceSet(null, knownSet1id, fileType.getId()); + fail("we should get an exception here"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex){ + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test checking invalid type try { EamDb.getInstance().isValueInReferenceSet(knownHash1, knownSet1id, emailType.getId()); - Assert.fail("isValueInReferenceSet failed to throw exception for invalid type"); + fail("isValueInReferenceSet failed to throw exception for invalid type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test known bad with notable data try { assertTrue("isArtifactKnownBadByReference returned false for notable value", EamDb.getInstance().isArtifactKnownBadByReference(fileType, notableHash1)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1850,7 +2014,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { assertFalse("isArtifactKnownBadByReference returned true for known value", EamDb.getInstance().isArtifactKnownBadByReference(fileType, knownHash1)); - } catch (EamDbException ex) { + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } @@ -1858,35 +2022,46 @@ public class CentralRepoDatamodelTest extends TestCase { // Test known bad with non-existent data try { assertFalse("isArtifactKnownBadByReference returned true for non-existent value", - EamDb.getInstance().isArtifactKnownBadByReference(fileType, "abcdef")); - } catch (EamDbException ex) { + EamDb.getInstance().isArtifactKnownBadByReference(fileType, randomHash())); + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } // Test known bad with null hash try { - assertFalse("isArtifactKnownBadByReference returned true for null value", - EamDb.getInstance().isArtifactKnownBadByReference(fileType, null)); + EamDb.getInstance().isArtifactKnownBadByReference(fileType, null); + fail("we should have thrown an exception"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test known bad with null type try { EamDb.getInstance().isArtifactKnownBadByReference(null, knownHash1); - Assert.fail("isArtifactKnownBadByReference failed to throw exception from null type"); + fail("isArtifactKnownBadByReference failed to throw exception from null type"); } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test known bad with invalid type try { - assertFalse("isArtifactKnownBadByReference returned true for invalid type", EamDb.getInstance().isArtifactKnownBadByReference(emailType, null)); + EamDb.getInstance().isArtifactKnownBadByReference(emailType, null); + fail("should get an exception here"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); + } catch (CorrelationAttributeNormalizationException ex) { + //this is expected + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } @@ -1960,9 +2135,10 @@ public class CentralRepoDatamodelTest extends TestCase { try { EamGlobalSet temp = new EamGlobalSet(org1.getOrgID(), set1name, "1.0", TskData.FileKnown.BAD, false, fileType); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from duplicate name/version pair"); + fail("newReferenceSet failed to throw exception from duplicate name/version pair"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a reference set with the same name but different version @@ -1979,43 +2155,47 @@ public class CentralRepoDatamodelTest extends TestCase { try { EamGlobalSet temp = new EamGlobalSet(5000, "tempName", "", TskData.FileKnown.BAD, false, fileType); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from invalid org ID"); + fail("newReferenceSet failed to throw exception from invalid org ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a reference set with null name try { EamGlobalSet temp = new EamGlobalSet(org2.getOrgID(), null, "", TskData.FileKnown.BAD, false, fileType); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from null name"); + fail("newReferenceSet failed to throw exception from null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a reference set with null version try { EamGlobalSet temp = new EamGlobalSet(org2.getOrgID(), "tempName", null, TskData.FileKnown.BAD, false, fileType); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from null version"); + fail("newReferenceSet failed to throw exception from null version"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a reference set with null file known status try { EamGlobalSet temp = new EamGlobalSet(org2.getOrgID(), "tempName", "", null, false, fileType); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from null file known status"); + fail("newReferenceSet failed to throw exception from null file known status"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a reference set with null file type try { EamGlobalSet temp = new EamGlobalSet(org2.getOrgID(), "tempName", "", TskData.FileKnown.BAD, false, null); EamDb.getInstance().newReferenceSet(temp); - Assert.fail("newReferenceSet failed to throw exception from null file type"); + fail("newReferenceSet failed to throw exception from null file type"); } catch (EamDbException ex) { // This is the expected behavior } @@ -2125,9 +2305,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test null argument to getAllReferenceSets try { EamDb.getInstance().getAllReferenceSets(null); - Assert.fail("getAllReferenceSets failed to throw exception from null type argument"); + fail("getAllReferenceSets failed to throw exception from null type argument"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test deleting an existing reference set @@ -2172,9 +2353,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting reference set organization for non-existent reference set try { EamDb.getInstance().getReferenceSetOrganization(4567); - Assert.fail("getReferenceSetOrganization failed to throw exception for invalid reference set ID"); + fail("getReferenceSetOrganization failed to throw exception for invalid reference set ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } @@ -2217,7 +2399,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationDataSource temp = new CorrelationDataSource(case2, dataSourceAid, dataSourceAname); EamDb.getInstance().newDataSource(temp); - Assert.fail("newDataSource did not throw exception from duplicate data source"); + fail("newDataSource did not throw exception from duplicate data source"); } catch (EamDbException ex) { // This is the expected behavior } @@ -2237,27 +2419,30 @@ public class CentralRepoDatamodelTest extends TestCase { CorrelationCase correlationCase = new CorrelationCase("1", "test"); CorrelationDataSource temp = new CorrelationDataSource(correlationCase, "tempID", "tempName"); EamDb.getInstance().newDataSource(temp); - Assert.fail("newDataSource did not throw exception from invalid case ID"); + fail("newDataSource did not throw exception from invalid case ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a data source with null device ID try { CorrelationDataSource temp = new CorrelationDataSource(case2, null, "tempName"); EamDb.getInstance().newDataSource(temp); - Assert.fail("newDataSource did not throw exception from null device ID"); + fail("newDataSource did not throw exception from null device ID"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a data source with null name try { CorrelationDataSource temp = new CorrelationDataSource(case2, "tempID", null); EamDb.getInstance().newDataSource(temp); - Assert.fail("newDataSource did not throw exception from null name"); + fail("newDataSource did not throw exception from null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting a data source with valid case and ID @@ -2281,9 +2466,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting a data source with a null case try { EamDb.getInstance().getDataSource(null, dataSourceAid); - Assert.fail("getDataSource did not throw exception from null case"); + fail("getDataSource did not throw exception from null case"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting a data source with null ID @@ -2378,18 +2564,20 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationCase tempCase = new CorrelationCase(null, "nullUuidCase"); EamDb.getInstance().newCase(tempCase); - Assert.fail("newCase did not throw expected exception from null uuid"); + fail("newCase did not throw expected exception from null uuid"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test null name try { CorrelationCase tempCase = new CorrelationCase("nullCaseUuid", null); EamDb.getInstance().newCase(tempCase); - Assert.fail("newCase did not throw expected exception from null name"); + fail("newCase did not throw expected exception from null name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test creating a case with an already used UUID @@ -2421,9 +2609,10 @@ public class CentralRepoDatamodelTest extends TestCase { try { Case nullCase = null; EamDb.getInstance().newCase(nullCase); - Assert.fail("newCase did not throw expected exception from null case"); + fail("newCase did not throw expected exception from null case"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test update case @@ -2471,9 +2660,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test update case with null case try { EamDb.getInstance().updateCase(null); - Assert.fail("updateCase did not throw expected exception from null case"); + fail("updateCase did not throw expected exception from null case"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Test getting a case from an Autopsy case @@ -2554,9 +2744,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test bulk case insert with null list try { EamDb.getInstance().bulkInsertCases(null); - Assert.fail("bulkInsertCases did not throw expected exception from null list"); + fail("bulkInsertCases did not throw expected exception from null list"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } finally { try { @@ -2608,7 +2799,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test null name try { EamDb.getInstance().newDbInfo(null, value1); - Assert.fail("newDbInfo did not throw expected exception from null name"); + fail("newDbInfo did not throw expected exception from null name"); } catch (EamDbException ex) { // This is the expected behavior } @@ -2616,9 +2807,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test null value try { EamDb.getInstance().newDbInfo(name2, null); - Assert.fail("newDbInfo did not throw expected exception from null value"); + fail("newDbInfo did not throw expected exception from null value"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Try getting the dbInfo entry that should exist @@ -2660,9 +2852,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Try updating an existing value to null try { EamDb.getInstance().updateDbInfo(name1, null); - Assert.fail("updateDbInfo did not throw expected exception from null value"); + fail("updateDbInfo did not throw expected exception from null value"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } // Try updating a null name @@ -2677,11 +2870,33 @@ public class CentralRepoDatamodelTest extends TestCase { // Try updating the value for a non-existant name try { EamDb.getInstance().updateDbInfo(name1, null); - Assert.fail("updateDbInfo did not throw expected exception from non-existent name"); + fail("updateDbInfo did not throw expected exception from non-existent name"); } catch (EamDbException ex) { // This is the expected behavior + assertTrue(THIS_IS_THE_EXPECTED_BEHAVIOR, true); } } + private static final String THIS_IS_THE_EXPECTED_BEHAVIOR = "This is the expected behavior."; + + private static String randomHash() { + + String[] chars = {"a", "b", "c", "d", "e", "f", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + Random random = new Random(); + IntStream ints = random.ints(32, 0, chars.length - 1); + + Iterator it = ints.iterator(); + + StringBuilder md5 = new StringBuilder(32); + + while(it.hasNext()){ + Integer i = it.next(); + String character = chars[i]; + md5.append(character); + } + + return md5.toString(); + } public class AttributeInstanceTableCallback implements InstanceTableCallback { @@ -2710,7 +2925,5 @@ public class CentralRepoDatamodelTest extends TestCase { public int getCounterNamingConvention(){ return counterNamingConvention; } - } - -} +} \ No newline at end of file diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizerTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizerTest.java new file mode 100644 index 0000000000..76d28e7ea6 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizerTest.java @@ -0,0 +1,315 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.centralrepository.datamodel; + +import junit.framework.Test; +import org.junit.Assert; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; + +/** + * Tests for validation on each correlation attribute type. + */ +public class CorrelationAttributeNormalizerTest extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(CorrelationAttributeNormalizerTest.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + public CorrelationAttributeNormalizerTest(String name) { + super(name); + } + + public void testValidateMd5() { + final String aValidHash = "e34a8899ef6468b74f8a1048419ccc8b"; //should pass + final String anInValidHash = "e34asdfa8899ef6468b74f8a1048419ccc8b"; //should fail + final String aValidHashWithCaps = "E34A8899EF6468B74F8A1048419CCC8B"; //should pass and be lowered + final String emptyHash = ""; //should fail + final String nullHash = null; //should fail + + final int FILES_TYPE_ID = CorrelationAttributeInstance.FILES_TYPE_ID; + + try { + assertTrue("This hash should just work", CorrelationAttributeNormalizer.normalize(FILES_TYPE_ID, aValidHash).equals(aValidHash)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue("This hash just needs to be converted to lower case", CorrelationAttributeNormalizer.normalize(CorrelationAttributeInstance.FILES_TYPE_ID, aValidHashWithCaps).equals(aValidHash)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(FILES_TYPE_ID, anInValidHash); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(FILES_TYPE_ID, emptyHash); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(FILES_TYPE_ID, nullHash); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + } + private static final String WE_EXPECT_AN_EXCEPTION_HERE = "We expect an exception here."; + private static final String THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION = "This should have thrown an exception."; + + public void testValidateDomain() { + final String goodDomainOne = "www.test.com"; //should pass + final String badDomainTwo = "http://www.test.com"; //should fail (includes protocol) + final String goodDomainThree = "test.com"; //should pass + final String badDomainFour = "http://1270.0.1"; //should fail + final String badDomainFive = "?>\\/)(*&.com"; //should fail + final String badDomainSix = null; //should fail + final String badDomainSeven = ""; //should fail + final String badDomainEight = "HTTP://tests.com"; //should fail + final String badDomainNine = "http://www.test.com/aPage?aQuestion=aParam&anotherQuestion=anotherParam"; //should fail + final String goodDomainTen = "WWW.TEST.COM"; //should pass but be lowered + final String goodDomainEleven = "TEST.COM"; //should pass but be lowered + + final int DOMAIN_TYPE_ID = CorrelationAttributeInstance.DOMAIN_TYPE_ID; + + try { + assertTrue(THIS_DOMAIN_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, goodDomainOne).equals(goodDomainOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainTwo); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + assertTrue(THIS_DOMAIN_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, goodDomainThree).equals(goodDomainThree)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_DOMAIN_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainFour).equals(badDomainFour)); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainFive); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainSix); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainSeven); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainEight); + fail("This should have thrown an exception"); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, badDomainNine); + fail("This should have thrown an exception"); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + assertTrue(THIS_DOMAIN_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, goodDomainTen).equals(goodDomainTen.toLowerCase())); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_DOMAIN_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(DOMAIN_TYPE_ID, goodDomainEleven).equals(goodDomainEleven.toLowerCase())); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + } + private static final String THIS_DOMAIN_SHOULD_PASS = "This domain should pass."; + + public void testValidateEmail() { + final String goodEmailOne = "bsweeney@cipehrtechsolutions.com"; //should pass + final String goodEmailTwo = "BSWEENEY@ciphertechsolutions.com"; //should pass and be lowered + final String badEmailThree = ""; //should fail + final String badEmailFour = null; //should fail + final String badEmailFive = "asdf"; //should fail + final String goodEmailSix = "asdf@asdf"; //TODO looks bad but the lib accepts it... + final String badEmailSeven = "asdf.asdf"; //should + + final int EMAIL_TYPE_ID = CorrelationAttributeInstance.EMAIL_TYPE_ID; + + try { + assertTrue("This email should pass.", CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, goodEmailOne).equals(goodEmailOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue("This email should pass.", CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, goodEmailTwo).equals(goodEmailTwo.toLowerCase())); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, badEmailThree); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, badEmailFour); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, badEmailFive); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { //TODO consider a better library? + assertTrue("This email should pass", CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, goodEmailSix).equals(goodEmailSix)); + } catch (CorrelationAttributeNormalizationException ex) { + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(EMAIL_TYPE_ID, badEmailSeven); + fail(THIS_SHOULD_HAVE_THROWN_AN_EXCEPTION); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + } + + public void testValidatePhone() { + final String goodPnOne = "19784740486"; + final String goodPnTwo = "1(978) 474-0486"; + final String goodPnThree = "+19784740486"; + final String goodPnFour = "1 978-474-0486"; + final String badPnFive = "9879879819784740486"; + final String goodPnSix = "+1(978) 474-0486"; + final String goodPnSeven = "+1(978) 474-0486"; + final String badPnEight = "asdfasdfasdf"; + final String badPnNine = "asdf19784740486adsf"; + + final int PHONE_TYPE_ID = CorrelationAttributeInstance.PHONE_TYPE_ID; + + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnOne).equals(goodPnOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnTwo).equals(goodPnOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnThree).equals(goodPnThree)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnFour).equals(goodPnOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, badPnFive); + //fail("This should have thrown an exception."); //this will eventually pass when we do a better job at this + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnSix).equals(goodPnThree)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + assertTrue(THIS_PHONE_NUMBER_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, goodPnSeven).equals(goodPnThree)); + } catch (CorrelationAttributeNormalizationException ex) { + Exceptions.printStackTrace(ex); + fail(ex.getMessage()); + } + try { + CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, badPnEight); + fail("This should have thrown an exception."); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + try { + CorrelationAttributeNormalizer.normalize(PHONE_TYPE_ID, badPnNine); + fail("This should have thrown an exception."); + } catch (CorrelationAttributeNormalizationException ex) { + assertTrue(WE_EXPECT_AN_EXCEPTION_HERE, true); + } + } + private static final String THIS_PHONE_NUMBER_SHOULD_PASS = "This phone number should pass."; + + public void testValidateUsbId() { + //TODO will need to be updated once usb validation does somethign interesting + final String goodIdOne = "0202:AAFF"; //should pass + /*final String goodIdTwo = "0202:aaff"; //should pass + final String badIdThree = "0202:axxf"; //should fail + final String badIdFour = ""; //should fail + final String badIdFive = null; //should fail + final String goodIdSix = "0202 AAFF"; //should pass + final String goodIdSeven = "0202AAFF"; //should pass + final String goodIdEight = "0202-AAFF"; //should pass*/ + + final int USBID_TYPE_ID = CorrelationAttributeInstance.USBID_TYPE_ID; + + try { + assertTrue(THIS_USB_ID_SHOULD_PASS, CorrelationAttributeNormalizer.normalize(USBID_TYPE_ID, goodIdOne).equals(goodIdOne)); + } catch (CorrelationAttributeNormalizationException ex) { + Assert.fail(ex.getMessage()); + } + } + private static final String THIS_USB_ID_SHOULD_PASS = "This USB ID should pass."; +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/CommonAttributeSearchInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/CommonAttributeSearchInterCaseTests.java new file mode 100644 index 0000000000..966d0b8bce --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/CommonAttributeSearchInterCaseTests.java @@ -0,0 +1,174 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.commonfilessearch; + +import java.nio.file.Path; +import java.sql.SQLException; +import java.util.Map; +import junit.framework.Assert; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.AllInterCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.CASE1; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.CASE2; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.CASE3; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.CASE4; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.verifyInstanceCount; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Search for commonality in different sorts of attributes (files, usb devices, + * emails, domains). Observe that frequency filtering works for various types. + * + * TODO (JIRA-4166): The following tests are commented out because the + * functional test framework needs to be able to configure the keyword search + * ingest module to produce instances of the correlation attributes for the + * tests. This cannot be easily done at present because the keyword search + * module resides in an NBM with a dependency on the Autopsy-Core NBM; the + * otherwise obvious solution of publicly exposing the keyword search module + * settings fails due to a circular dependency. + * + */ +public class CommonAttributeSearchInterCaseTests extends NbTestCase { + + private final InterCaseTestUtils utils; + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(CommonAttributeSearchInterCaseTests.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + public CommonAttributeSearchInterCaseTests(String name) { + super(name); + this.utils = new InterCaseTestUtils(this); + } + + @Override + public void setUp() { + this.utils.clearTestDir(); + try { + this.utils.enableCentralRepo(); + + String[] cases = new String[]{ + CASE1, + CASE2, + CASE3, + CASE4}; + + Path[][] paths = { + {this.utils.attrCase1Path}, + {this.utils.attrCase2Path}, + {this.utils.attrCase3Path}, + {this.utils.attrCase4Path}}; + + this.utils.createCases(cases, paths, this.utils.getIngestSettingsForKitchenSink(), InterCaseTestUtils.CASE1); + } catch (TskCoreException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex.getMessage()); + } + } + + @Override + public void tearDown() { + this.utils.clearTestDir(); + this.utils.tearDown(); + } + + /** + * Run a search on the given type and ensure that all results are off that + * type. + * + * No frequency filtering applied. + * + * @param type + */ + private void assertResultsAreOfType(CorrelationAttributeInstance.Type type) { + + try { + Map dataSources = this.utils.getDataSourceMap(); + + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, type, 0); + + CommonAttributeSearchResults metadata = builder.findMatches(); + + metadata.size(); + + assertFalse(verifyInstanceCount(metadata, 0)); + + assertTrue(this.utils.areAllResultsOfType(metadata, type)); + + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex.getMessage()); + } + } + + /** + * Test that a search for each type returns results of that type only. + */ + public void testOne() { +// assertResultsAreOfType(this.utils.USB_ID_TYPE); +// assertResultsAreOfType(this.utils.DOMAIN_TYPE); +// assertResultsAreOfType(this.utils.FILE_TYPE); +// assertResultsAreOfType(this.utils.EMAIL_TYPE); +// assertResultsAreOfType(this.utils.PHONE_TYPE); + } + + /** + * Test that the frequency filter behaves reasonably for attributes other + * than the file type. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + AbstractCommonAttributeSearcher builder; + CommonAttributeSearchResults metadata; + + builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 100); + metadata = builder.findMatches(); + metadata.size(); + //assertTrue("This should yield 13 results.", verifyInstanceCount(metadata, 13)); + + builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 20); + metadata = builder.findMatches(); + metadata.size(); + //assertTrue("This should yield no results.", verifyInstanceCount(metadata, 0)); + + builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 90); + metadata = builder.findMatches(); + metadata.size(); + //assertTrue("This should yield 2 results.", verifyInstanceCount(metadata, 2)); + + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex.getMessage()); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java index a425ecbcc8..8f4f96e7a3 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -19,6 +19,7 @@ */ package org.sleuthkit.autopsy.commonfilessearch; +import java.nio.file.Path; import java.sql.SQLException; import java.util.Map; import junit.framework.Test; @@ -27,6 +28,7 @@ import org.netbeans.junit.NbTestCase; import org.openide.util.Exceptions; import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; import org.sleuthkit.autopsy.commonfilesearch.AllInterCaseCommonAttributeSearcher; @@ -37,7 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Tests with case 3 as the current case. - * + * * If I use the search all cases option: One node for Hash A (1_1_A.jpg, * 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case 1: One node for * Hash A (1_1_A.jpg, 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case @@ -45,27 +47,38 @@ import org.sleuthkit.datamodel.TskCoreException; * all data sources: One node for Hash C (3_1_C.jpg, 3_2_C.jpg) */ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { - + private final InterCaseTestUtils utils; - + public static Test suite() { NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileTypeInterCaseTests.class). clusters(".*"). enableModules(".*"); return conf.suite(); } - + public IngestedWithHashAndFileTypeInterCaseTests(String name) { super(name); this.utils = new InterCaseTestUtils(this); } - + @Override - public void setUp(){ + public void setUp() { this.utils.clearTestDir(); try { this.utils.enableCentralRepo(); - this.utils.createCases(this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3); + + String[] cases = new String[]{ + CASE1, + CASE2, + CASE3}; + + Path[][] paths = { + {this.utils.case1DataSet1Path, this.utils.case1DataSet2Path}, + {this.utils.case2DataSet1Path, this.utils.case2DataSet2Path}, + {this.utils.case3DataSet1Path, this.utils.case3DataSet2Path}}; + + this.utils.createCases(cases, paths, this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3); } catch (TskCoreException | EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); @@ -73,118 +86,115 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { } @Override - public void tearDown(){ + public void tearDown() { this.utils.clearTestDir(); this.utils.tearDown(); } - + /** * Search All cases with no file type filtering. */ public void testOne() { try { Map dataSources = this.utils.getDataSourceMap(); - + //note that the params false and false are presently meaningless because that feature is not supported yet - AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, 0); - - CommonAttributeSearchResults metadata = builder.findFiles(); - + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.FILE_TYPE, 0); + CommonAttributeSearchResults metadata = builder.findMatches(); + assertTrue("Results should not be empty", metadata.size() != 0); - + //case 1 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); - + //case 1 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); - + //case 2 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); - + //case 2 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1)); - + //case 3 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); - + //case 3 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); - - + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { - Exceptions.printStackTrace(ex); + Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } } - + /** * Search All cases with no file type filtering. */ public void testTwo() { try { Map dataSources = this.utils.getDataSourceMap(); - + int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2); - - AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false, 0); - - CommonAttributeSearchResults metadata = builder.findFiles(); + CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); + AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false, fileType, 0); + CommonAttributeSearchResults metadata = builder.findMatches(); + assertTrue("Results should not be empty", metadata.size() != 0); - + //case 1 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); - + //case 1 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); - + //case 2 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); - + //case 2 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1)); - + //case 3 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); - + //case 3 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); - - + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { - Exceptions.printStackTrace(ex); + Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } } /** - * We should be able to observe that certain files o no longer returned + * We should be able to observe that certain files are no longer returned * in the result set since they do not appear frequently enough. */ public void testThree(){ @@ -192,34 +202,35 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); //note that the params false and false are presently meaningless because that feature is not supported yet - AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, 50); + CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, fileType, 50); - CommonAttributeSearchResults metadata = builder.findFiles(); + CommonAttributeSearchResults metadata = builder.findMatches(); assertTrue("Results should not be empty", metadata.size() != 0); //case 1 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 0)); //case 1 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 0)); //case 2 data set 1 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); //case 2 data set 2 - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1)); //case 3 data set 1 - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); @@ -227,7 +238,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { //case 3 data set 2 assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); - assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java index f717e8a670..4a423c800e 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java @@ -100,7 +100,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); @@ -141,7 +141,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -182,7 +182,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, true, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -224,7 +224,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long first = getDataSourceIdByName(SET1, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -266,7 +266,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long first = getDataSourceIdByName(SET1, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, true, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -308,7 +308,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long first = getDataSourceIdByName(SET1, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, true, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -350,7 +350,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long second = getDataSourceIdByName(SET2, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(second, dataSources, false, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -391,7 +391,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long last = getDataSourceIdByName(SET4, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(last, dataSources, false, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -432,7 +432,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Long third = getDataSourceIdByName(SET3, dataSources); AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, false, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java index dbca68d586..f10721838c 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java @@ -102,7 +102,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); @@ -126,7 +126,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { Long third = IntraCaseTestUtils.getDataSourceIdByName(IntraCaseTestUtils.SET3, dataSources); IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index 2791f725a7..de9b537ff6 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -25,8 +25,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.netbeans.junit.NbTestCase; @@ -59,6 +59,14 @@ import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory; +import org.sleuthkit.autopsy.modules.embeddedfileextractor.EmbeddedFileExtractorModuleFactory; +import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory; +import org.sleuthkit.autopsy.modules.fileextmismatch.FileExtMismatchDetectorModuleFactory; +import org.sleuthkit.autopsy.modules.iOS.iOSModuleFactory; +import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory; +import org.sleuthkit.autopsy.modules.photoreccarver.PhotoRecCarverIngestModuleFactory; +import org.sleuthkit.autopsy.modules.vmextractor.VMExtractorIngestModuleFactory; import org.sleuthkit.datamodel.AbstractFile; /** @@ -73,23 +81,54 @@ import org.sleuthkit.datamodel.AbstractFile; * Description of Test Data: (Note: files of the same name and extension are * identical; files of the same name and differing extension are not identical.) * - * Case 1 +Data Set 1 - Hash-0.dat [testFile of size 0] - Hash-A.jpg - - * Hash-A.pdf +Data Set2 - Hash-0.dat [testFile of size 0] - Hash-A.jpg - - * Hash-A.pdf Case 2 +Data Set 1 - Hash-B.jpg - Hash-B.pdf +Data Set 2 - - * Hash-A.jpg - Hash-A.pdf - Hash_D.doc Case 3 +Data Set 1 - Hash-A.jpg - - * Hash-A.pdf - Hash-C.jpg - Hash-C.pdf - Hash-D.jpg +Data Set 2 - Hash-C.jpg - - * Hash-C.pdf - Hash-D.doc + * Case 1 + * +Data Set 1 + * - Hash-0.dat [testFile of size 0] + * - Hash-A.jpg + * - Hash-A.pdf + * + * +Data Set2 + * - Hash-0.dat [testFile of size 0] + * - Hash-A.jpg + * - Hash-A.pdf + * + * Case 2 + * +Data Set 1 + * - Hash-B.jpg + * - Hash-B.pdf + * +Data Set 2 + * - Hash-A.jpg + * - Hash-A.pdf + * - Hash_D.doc + * + * Case 3 + * +Data Set 1 + * - Hash-A.jpg + * - Hash-A.pdf + * - Hash-C.jpg + * - Hash-C.pdf + * - Hash-D.jpg + * +Data Set 2 + * - Hash-C.jpg + * - Hash-C.pdf + * - Hash-D.doc * * Frequency Breakdown (ratio of datasources a given file appears in to total * number of datasources): * - * Hash-0.dat - moot; these are always excluded Hash-A.jpg - 4/6 Hash-A.pdf - - * 4/6 Hash-B.jpg - 1/6 Hash-B.pdf - 1/6 Hash-C.jpg - 2/6 Hash-C.pdf - 2/6 - * Hash_D.doc - 2/6 Hash-D.jpg - 1/6 + * Hash-0.dat - moot; these are always excluded + * Hash-A.jpg - 4/6 + * Hash-A.pdf - 4/6 + * Hash-B.jpg - 1/6 + * Hash-B.pdf - 1/6 + * Hash-C.jpg - 2/6 + * Hash-C.pdf - 2/6 + * Hash_D.doc - 2/6 + * Hash-D.jpg - 1/6 * */ class InterCaseTestUtils { - + private static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), "InterCaseCommonFilesSearchTest"); private static final String CR_DB_NAME = "testcentralrepo.db"; @@ -121,13 +160,31 @@ class InterCaseTestUtils { static final String CASE2_DATASET_2 = "c2ds2_v1.vhd"; static final String CASE3_DATASET_1 = "c3ds1_v1.vhd"; static final String CASE3_DATASET_2 = "c3ds2_v1.vhd"; - + + final Path attrCase1Path; + final Path attrCase2Path; + final Path attrCase3Path; + final Path attrCase4Path; + + static final String ATTR_CASE1 = "CommonFilesAttrs_img1_v1.vhd"; + static final String ATTR_CASE2 = "CommonFilesAttrs_img2_v1.vhd"; + static final String ATTR_CASE3 = "CommonFilesAttrs_img3_v1.vhd"; + static final String ATTR_CASE4 = "CommonFilesAttrs_img4_v1.vhd"; + private final ImageDSProcessor imageDSProcessor; private final IngestJobSettings hashAndFileType; private final IngestJobSettings hashAndNoFileType; + private final IngestJobSettings kitchenShink; + private final DataSourceLoader dataSourceLoader; - + + CorrelationAttributeInstance.Type FILE_TYPE; + CorrelationAttributeInstance.Type DOMAIN_TYPE; + CorrelationAttributeInstance.Type USB_ID_TYPE; + CorrelationAttributeInstance.Type EMAIL_TYPE; + CorrelationAttributeInstance.Type PHONE_TYPE; + InterCaseTestUtils(NbTestCase testCase) { this.case1DataSet1Path = Paths.get(testCase.getDataDir().toString(), CASE1_DATASET_1); @@ -136,13 +193,32 @@ class InterCaseTestUtils { this.case2DataSet2Path = Paths.get(testCase.getDataDir().toString(), CASE2_DATASET_2); this.case3DataSet1Path = Paths.get(testCase.getDataDir().toString(), CASE3_DATASET_1); this.case3DataSet2Path = Paths.get(testCase.getDataDir().toString(), CASE3_DATASET_2); + + this.attrCase1Path = Paths.get(testCase.getDataDir().toString(), ATTR_CASE1); + this.attrCase2Path = Paths.get(testCase.getDataDir().toString(), ATTR_CASE2); + this.attrCase3Path = Paths.get(testCase.getDataDir().toString(), ATTR_CASE3); + this.attrCase4Path = Paths.get(testCase.getDataDir().toString(), ATTR_CASE4); this.imageDSProcessor = new ImageDSProcessor(); - final IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + final IngestModuleTemplate exifTemplate = IngestUtils.getIngestModuleTemplate(new ExifParserModuleFactory()); + final IngestModuleTemplate iOsTemplate = IngestUtils.getIngestModuleTemplate(new iOSModuleFactory()); + final IngestModuleTemplate embeddedFileExtractorTemplate = IngestUtils.getIngestModuleTemplate(new EmbeddedFileExtractorModuleFactory()); + final IngestModuleTemplate interestingItemsTemplate = IngestUtils.getIngestModuleTemplate(new InterestingItemsIngestModuleFactory()); final IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); + final IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + final IngestModuleTemplate vmExtractorTemplate = IngestUtils.getIngestModuleTemplate(new VMExtractorIngestModuleFactory()); + final IngestModuleTemplate photoRecTemplate = IngestUtils.getIngestModuleTemplate(new PhotoRecCarverIngestModuleFactory()); + final IngestModuleTemplate e01VerifierTemplate = IngestUtils.getIngestModuleTemplate(new E01VerifierModuleFactory()); final IngestModuleTemplate eamDbTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.centralrepository.ingestmodule.IngestModuleFactory()); - + final IngestModuleTemplate fileExtMismatchDetectorTemplate = IngestUtils.getIngestModuleTemplate(new FileExtMismatchDetectorModuleFactory()); + //TODO we need to figure out how to get ahold of these objects because they are required for properly filling the CR with test data +// final IngestModuleTemplate objectDetectorTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.experimental.objectdetection.ObjectDetectionModuleFactory()); +// final IngestModuleTemplate emailParserTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory()); +// final IngestModuleTemplate recentActivityTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory()); +// final IngestModuleTemplate keywordSearchTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleFactory()); + + //hash and mime ArrayList hashAndMimeTemplate = new ArrayList<>(2); hashAndMimeTemplate.add(hashLookupTemplate); hashAndMimeTemplate.add(mimeTypeLookupTemplate); @@ -150,13 +226,56 @@ class InterCaseTestUtils { this.hashAndFileType = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.FILES_ONLY, hashAndMimeTemplate); + //hash and no mime ArrayList hashAndNoMimeTemplate = new ArrayList<>(1); hashAndNoMimeTemplate.add(hashLookupTemplate); hashAndMimeTemplate.add(eamDbTemplate); this.hashAndNoFileType = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.FILES_ONLY, hashAndNoMimeTemplate); + //kitchen sink + ArrayList kitchenSink = new ArrayList<>(); + kitchenSink.add(exifTemplate); + kitchenSink.add(iOsTemplate); + kitchenSink.add(embeddedFileExtractorTemplate); + kitchenSink.add(interestingItemsTemplate); + kitchenSink.add(mimeTypeLookupTemplate); + kitchenSink.add(hashLookupTemplate); + kitchenSink.add(vmExtractorTemplate); + kitchenSink.add(photoRecTemplate); + kitchenSink.add(e01VerifierTemplate); + kitchenSink.add(eamDbTemplate); + kitchenSink.add(fileExtMismatchDetectorTemplate); + //TODO this list should probably be populated by way of loading the appropriate modules based on finding all of the @ServiceProvider(service = IngestModuleFactory.class) types +// kitchenSink.add(objectDetectorTemplate); +// kitchenSink.add(emailParserTemplate); +// kitchenSink.add(recentActivityTemplate); +// kitchenSink.add(keywordSearchTemplate); + + this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink); + this.dataSourceLoader = new DataSourceLoader(); + + try { + Collection types = CorrelationAttributeInstance.getDefaultCorrelationTypes(); + + //TODO use ids instead of strings + FILE_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Files")).findAny().get(); + DOMAIN_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Domains")).findAny().get(); + USB_ID_TYPE = types.stream().filter(type -> type.getDisplayName().equals("USB Devices")).findAny().get(); + EMAIL_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Email Addresses")).findAny().get(); + PHONE_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Phone Numbers")).findAny().get(); + + } catch (EamDbException ex) { + Assert.fail(ex.getMessage()); + + //none of this really matters but satisfies the compiler + FILE_TYPE = null; + DOMAIN_TYPE = null; + USB_ID_TYPE = null; + EMAIL_TYPE = null; + PHONE_TYPE = null; + } } void clearTestDir() { @@ -201,6 +320,10 @@ class InterCaseTestUtils { IngestJobSettings getIngestSettingsForHashAndNoFileType() { return this.hashAndNoFileType; } + + IngestJobSettings getIngestSettingsForKitchenSink(){ + return this.kitchenShink; + } void enableCentralRepo() throws EamDbException { @@ -214,7 +337,7 @@ class InterCaseTestUtils { crSettings.initializeDatabaseSchema(); crSettings.insertDefaultDatabaseContent(); - crSettings.saveSettings(); + crSettings.saveSettings(); EamDbUtil.setUseCentralRepo(true); EamDbPlatformEnum.setSelectedPlatform(EamDbPlatformEnum.SQLITE.name()); @@ -222,33 +345,33 @@ class InterCaseTestUtils { } /** - * Create 3 cases and ingest each with the given settings. Null settings are - * permitted but IngestUtils will not be run. + * Create the cases defined by caseNames and caseDataSourcePaths and ingest + * each with the given settings. Null settings are permitted but + * IngestUtils will not be run. + * + * The length of caseNames and caseDataSourcePaths should be the same, and + * cases should appear in the same order. * + * @param caseNames list case names + * @param caseDataSourcePaths two dimensional array listing the datasources in each case * @param ingestJobSettings HashLookup FileType etc... * @param caseReferenceToStore */ - Case createCases(IngestJobSettings ingestJobSettings, String caseReferenceToStore) throws TskCoreException { + Case createCases(String[] caseNames, Path[][] caseDataSourcePaths, IngestJobSettings ingestJobSettings, String caseReferenceToStore) throws TskCoreException { Case currentCase = null; - String[] cases = new String[]{ - CASE1, - CASE2, - CASE3}; - - Path[][] paths = { - {this.case1DataSet1Path, this.case1DataSet2Path}, - {this.case2DataSet1Path, this.case2DataSet2Path}, - {this.case3DataSet1Path, this.case3DataSet2Path}}; + if(caseNames.length != caseDataSourcePaths.length){ + Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of the values given in the 'cases' parameter.").getMessage()); + } String lastCaseName = null; Path[] lastPathsForCase = null; //iterate over the collections above, creating cases, and storing // just one of them for future reference - for (int i = 0; i < cases.length; i++) { - String caseName = cases[i]; - Path[] pathsForCase = paths[i]; + for (int i = 0; i < caseNames.length; i++) { + String caseName = caseNames[i]; + Path[] pathsForCase = caseDataSourcePaths[i]; if (caseName.equals(caseReferenceToStore)) { //put aside and do this one last so we can hang onto the case @@ -266,7 +389,7 @@ class InterCaseTestUtils { } if (currentCase == null) { - Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of: CASE1, CASE2, CASE3").getMessage()); + Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of the values given in the 'cases' parameter.").getMessage()); return null; } else { return currentCase; @@ -289,6 +412,27 @@ class InterCaseTestUtils { return null; } } + + static boolean verifyInstanceCount(CommonAttributeSearchResults searchDomain, int instanceCount){ + try { + int tally = 0; + + for (Map.Entry entry : searchDomain.getMetadata().entrySet()) { + entry.getValue().displayDelayedMetadata(); + for (CommonAttributeValue value : entry.getValue().getMetadataList()) { + + tally += value.getInstanceCount(); + } + } + + return tally == instanceCount; + + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex.getMessage()); + return false; + } + } static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount) { @@ -381,4 +525,29 @@ class InterCaseTestUtils { Assert.fail(ex.getMessage()); } } + + /** + * Is everything in metadata a result of the given attribute type? + * + * @param metadata + * @param attributeType + * @return true if yes, else false + */ + boolean areAllResultsOfType(CommonAttributeSearchResults metadata, CorrelationAttributeInstance.Type attributeType) { + try { + for(CommonAttributeValueList matches : metadata.getMetadata().values()){ + for(CommonAttributeValue value : matches.getMetadataList()){ + return value + .getInstances() + .stream() + .allMatch(inst -> inst.getCorrelationAttributeInstanceType().equals(attributeType)); + } + return false; + } + return false; + } catch (EamDbException ex) { + Assert.fail(ex.getMessage()); + return false; + } + } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java index 1105628340..5021a335ca 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java @@ -106,7 +106,7 @@ public class MatchesInAtLeastTwoSourcesIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java index dd81ba63c9..7edce65dda 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java @@ -81,7 +81,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); - CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + CommonAttributeSearchResults metadata = allSourcesBuilder.findMatches(); int resultCount = metadata.size(); assertEquals(resultCount, 0); @@ -101,7 +101,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { Long first = getDataSourceIdByName(SET1, dataSources); IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false, 0); - CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + CommonAttributeSearchResults metadata = singleSourceBuilder.findMatches(); int resultCount = metadata.size(); assertEquals(resultCount, 0); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestJobRunner.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestJobRunner.java index f3587ab29a..0abaebf48b 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestJobRunner.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestJobRunner.java @@ -39,7 +39,7 @@ public final class IngestJobRunner { * Runs an ingest job, blocking until the job is completed. * * @param dataSources The data sources for the ingest job. - * @param settings The settings for the ingst job + * @param settings The settings for the ingest job * * @return A list of ingest module start up error messages, empty if the job * was started sucessfully. @@ -68,7 +68,7 @@ public final class IngestJobRunner { } /** - * IngestRunner instances cannot be instatiated. + * IngestRunner instances cannot be instantiated. */ private IngestJobRunner() { }