From e41f38ec28934af75e3a3f43a21fce5a0a675b98 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Fri, 5 Jul 2019 10:34:14 -0400 Subject: [PATCH 01/11] Adding hash set attribute --- .../autopsy/filequery/FileSearch.java | 152 ++++++++++++++++++ .../autopsy/filequery/ResultFile.java | 77 ++++++++- 2 files changed, 228 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index f8f530f54d..56e9b6bfb9 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -746,6 +746,158 @@ class FileSearch { } } + /** + * Attribute for grouping/sorting by hash set lists + */ + static class HashHitsAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new HashHitsGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Concatenate the object IDs in the list of files + String objIdList = ""; // NON-NLS + for (ResultFile file : files) { + if ( ! objIdList.isEmpty()) { + objIdList += ","; // NON-NLS + } + objIdList += "\'" + file.getAbstractFile().getId() + "\'"; // NON-NLS + } + + // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // keyword list hits. + String selectQuery = "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS hash_set_name " + + "FROM blackboard_artifacts " + + "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id " + + "WHERE blackboard_attributes.artifact_type_id=\'" + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + "\' " + + "AND blackboard_attributes.attribute_type_id=\'" + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "\' " + + "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + + SetHashSetNamesCallback callback = new SetHashSetNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up hash set attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select query. Will add + * the keyword list names to the list of ResultFile objects. + */ + private static class SetHashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add keyword list names to + */ + SetHashSetNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getAbstractFile().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String hashSetName = rs.getString("hash_set_name"); // NON-NLS + + tempMap.get(objId).addHashSetName(hashSetName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or hash_set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get hash set names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing a hash hits group + */ + private static class HashHitsGroupKey extends GroupKey { + private final List hashSetNames; + private final String hashSetNamesString; + + @NbBundle.Messages({ + "FileSearch.HashHitsGroupKey.noHashHits=None", + }) + HashHitsGroupKey(ResultFile file) { + hashSetNames = file.getKeywordListNames(); + + if (hashSetNames.isEmpty()) { + hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits(); + } else { + hashSetNamesString = String.join(",", hashSetNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return hashSetNamesString; + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof HashHitsGroupKey) { + HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey)otherGroupKey; + + // Put the empty list at the end + if (hashSetNames.isEmpty()) { + if (otherHashHitsGroupKey.hashSetNames.isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherHashHitsGroupKey.hashSetNames.isEmpty()) { + return -1; + } + + return hashSetNamesString.compareTo(otherHashHitsGroupKey.hashSetNamesString); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this){ + return true; + } + + if (!(otherKey instanceof HashHitsGroupKey)) { + return false; + } + + HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey)otherKey; + return hashSetNamesString.equals(otherHashHitsGroupKey.hashSetNamesString); + } + + @Override + public int hashCode() { + return Objects.hash(hashSetNamesString); + } + } + + /** * Default attribute used to make one group */ diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java index c1e34aff18..f5b71c7016 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -33,6 +33,9 @@ class ResultFile { private final AbstractFile abstractFile; private FileSearchData.Frequency frequency; private final List keywordListNames; + private final List hashSetNames; + private final List tagNames; + private final List interestingSetNames; private FileType fileType; /** @@ -44,6 +47,9 @@ class ResultFile { this.abstractFile = abstractFile; this.frequency = FileSearchData.Frequency.UNKNOWN; keywordListNames = new ArrayList<>(); + hashSetNames = new ArrayList(); + tagNames = new ArrayList(); + interestingSetNames = new ArrayList(); fileType = FileType.OTHER; } @@ -100,12 +106,81 @@ class ResultFile { /** * Get the keyword list names for this file * - * @return the keyword lists that matched this file. + * @return the keyword list names that matched this file. */ List getKeywordListNames() { return keywordListNames; } + /** + * Add a hash set name that matched this file. + * + * @param keywordListName + */ + void addHashSetName (String hashSetName) { + if (! hashSetNames.contains(hashSetName)) { + hashSetNames.add(hashSetName); + } + + // Sort the list so the getHashHitNames() will be consistent regardless of the order added + Collections.sort(hashSetNames); + } + + /** + * Get the hash set names for this file + * + * @return the hash set names that matched this file. + */ + List getHashSetNames() { + return hashSetNames; + } + + /** + * Add a hash set name that matched this file. + * + * @param tagName + */ + void addTagName (String tagName) { + if (! tagNames.contains(tagName)) { + tagNames.add(tagName); + } + + // Sort the list so the getTagNames() will be consistent regardless of the order added + Collections.sort(tagNames); + } + + /** + * Get the tag names for this file + * + * @return the tag names that matched this file. + */ + List getTagNames() { + return tagNames; + } + + /** + * Add a hash set name that matched this file. + * + * @param interestingSetName + */ + void addInterestingSetName (String interestingSetName) { + if (! interestingSetNames.contains(interestingSetName)) { + interestingSetNames.add(interestingSetName); + } + + // Sort the list so the getInterestingSetNames() will be consistent regardless of the order added + Collections.sort(interestingSetNames); + } + + /** + * Get the interesting item set names for this file + * + * @return the interesting item set names that matched this file. + */ + List getInterestingSetNames() { + return interestingSetNames; + } + /** * Get the AbstractFile * From 124fd325d5052b7f45bf178d6e2a26ff6969859b Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 9 Jul 2019 07:56:34 -0400 Subject: [PATCH 02/11] Adding more attributes --- .../autopsy/filequery/Bundle.properties | 4 + .../filequery/Bundle.properties-MERGED | 23 +- .../autopsy/filequery/FileSearch.java | 452 ++++++++++++++++-- .../autopsy/filequery/FileSearchDialog.form | 224 +++++++-- .../autopsy/filequery/FileSearchDialog.java | 345 +++++++++++-- .../filequery/FileSearchFiltering.java | 214 ++++++++- .../autopsy/filequery/ResultFile.java | 34 +- 7 files changed, 1165 insertions(+), 131 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 44d31b4925..c330714cab 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -20,3 +20,7 @@ FileSearchDialog.searchButton.text=Search ResultsDialog.exitButton.text=Exit ResultsDialog.searchButton.text=Run another search FileSearchDialog.errorLabel.text=jLabel6 +FileSearchDialog.hashCheckBox.text=Hash Set +FileSearchDialog.intCheckBox.text=Interesting Items +FileSearchDialog.tagsCheckBox.text=Tags +FileSearchDialog.objCheckBox.text=Objects diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index b317dfb52f..a88262896f 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -4,15 +4,23 @@ CTL_FileSearchTestAction=Test file search FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1}) # {0} - Data source ID FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0}) +FileSearch.FileTagGroupKey.noSets=None FileSearch.GroupingAttributeType.datasource.displayName=Data source FileSearch.GroupingAttributeType.fileType.displayName=File type FileSearch.GroupingAttributeType.frequency.displayName=Past occurrences +FileSearch.GroupingAttributeType.hash.displayName=Hash set +FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting item set FileSearch.GroupingAttributeType.keywordList.displayName=Keyword list names FileSearch.GroupingAttributeType.none.displayName=None +FileSearch.GroupingAttributeType.object.displayName=Object detected FileSearch.GroupingAttributeType.parent.displayName=Parent folder FileSearch.GroupingAttributeType.size.displayName=Size +FileSearch.GroupingAttributeType.tag.displayName=File tag +FileSearch.HashHitsGroupKey.noHashHits=None +FileSearch.InterestingItemGroupKey.noSets=None FileSearch.KeywordListGroupKey.noKeywords=None FileSearch.NoGroupingGroupKey.allFiles=All Files +FileSearch.ObjectDetectedGroupKey.noSets=None FileSearchData.FileSize.large.displayName=200MB-1GB FileSearchData.FileSize.medium.displayName=50-200MB FileSearchData.FileSize.small.displayName=1-50MB @@ -48,6 +56,7 @@ FileSearchDialog.orderSizeRadioButton.text=Group Size FileSearchDialog.jLabel5.text=Order files by: FileSearchDialog.parentCheckBox.text=Parent FileSearchDialog.searchButton.text=Search +FileSearchFiltering.concatenateSetNamesForDisplay.comma=, # {0} - Data source name # {1} - Data source ID FileSearchFiltering.DataSourceFilter.datasource={0}({1}) @@ -60,10 +69,15 @@ FileSearchFiltering.FileTypeFilter.or=\ or # {0} - filters FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0} FileSearchFiltering.FrequencyFilter.or=\ or -FileSearchFiltering.KeywordListFilter.comma=, +# {0} - filters +FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0} +# {0} - filters +FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0} # {0} - filters FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} # {0} - filters +FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0} +# {0} - filters FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} FileSearchFiltering.ParentFilter.exact=(exact match) FileSearchFiltering.ParentFilter.or=\ or @@ -78,6 +92,9 @@ FileSearchFiltering.SizeFilter.or=\ or # {0} - Minimum bytes # {1} - Maximum bytes FileSearchFiltering.SizeFilter.range=({0} to {1}) +# {0} - tag names +FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0} +FileSearchFiltering.TagsFilter.or=\ or FileSorter.SortingMethod.datasource.displayName=By data source FileSorter.SortingMethod.filename.displayName=By file name FileSorter.SortingMethod.filesize.displayName=By file size @@ -89,3 +106,7 @@ ResultsDialog.dialogTitle.text=File search results ResultsDialog.exitButton.text=Exit ResultsDialog.searchButton.text=Run another search FileSearchDialog.errorLabel.text=jLabel6 +FileSearchDialog.hashCheckBox.text=Hash Set +FileSearchDialog.intCheckBox.text=Interesting Items +FileSearchDialog.tagsCheckBox.text=Tags +FileSearchDialog.objCheckBox.text=Objects diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index 56e9b6bfb9..2c917de708 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -157,6 +158,10 @@ class FileSearch { "FileSearch.GroupingAttributeType.size.displayName=Size", "FileSearch.GroupingAttributeType.datasource.displayName=Data source", "FileSearch.GroupingAttributeType.parent.displayName=Parent folder", + "FileSearch.GroupingAttributeType.hash.displayName=Hash set", + "FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting item set", + "FileSearch.GroupingAttributeType.tag.displayName=File tag", + "FileSearch.GroupingAttributeType.object.displayName=Object detected", "FileSearch.GroupingAttributeType.none.displayName=None", }) enum GroupingAttributeType { @@ -166,6 +171,10 @@ class FileSearch { FILE_SIZE(new FileSizeAttribute(), Bundle.FileSearch_GroupingAttributeType_size_displayName()), DATA_SOURCE(new DataSourceAttribute(), Bundle.FileSearch_GroupingAttributeType_datasource_displayName()), PARENT_PATH(new ParentPathAttribute(), Bundle.FileSearch_GroupingAttributeType_parent_displayName()), + HASH_LIST_NAME(new HashHitsAttribute(), Bundle.FileSearch_GroupingAttributeType_hash_displayName()), + INTERESTING_ITEM_SET(new InterestingItemAttribute(), Bundle.FileSearch_GroupingAttributeType_interestingItem_displayName()), + FILE_TAG(new FileTagAttribute(), Bundle.FileSearch_GroupingAttributeType_tag_displayName()), + OBJECT_DETECTED(new ObjectDetectedAttribute(), Bundle.FileSearch_GroupingAttributeType_object_displayName()), NO_GROUPING(new NoGroupingAttribute(), Bundle.FileSearch_GroupingAttributeType_none_displayName()); private final AttributeType attributeType; @@ -527,24 +536,11 @@ class FileSearch { @Override void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - - // Concatenate the object IDs in the list of files - String objIdList = ""; // NON-NLS - for (ResultFile file : files) { - if ( ! objIdList.isEmpty()) { - objIdList += ","; // NON-NLS - } - objIdList += "\'" + file.getAbstractFile().getId() + "\'"; // NON-NLS - } - + // Get pairs of (object ID, keyword list name) for all files in the list of files that have // keyword list hits. - String selectQuery = "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS keyword_list_name " + - "FROM blackboard_artifacts " + - "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id " + - "WHERE blackboard_attributes.artifact_type_id=\'" + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + "\' " + - "AND blackboard_attributes.attribute_type_id=\'" + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "\' " + - "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(files); try { @@ -583,12 +579,12 @@ class FileSearch { while (rs.next()) { try { Long objId = rs.getLong("object_id"); // NON-NLS - String keywordListName = rs.getString("keyword_list_name"); // NON-NLS + String keywordListName = rs.getString("set_name"); // NON-NLS tempMap.get(objId).addKeywordListName(keywordListName); } catch (SQLException ex) { - logger.log(Level.SEVERE, "Unable to get object_id or keyword_list_name from result set", ex); // NON-NLS + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS } } } catch (SQLException ex) { @@ -760,23 +756,10 @@ class FileSearch { void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - // Concatenate the object IDs in the list of files - String objIdList = ""; // NON-NLS - for (ResultFile file : files) { - if ( ! objIdList.isEmpty()) { - objIdList += ","; // NON-NLS - } - objIdList += "\'" + file.getAbstractFile().getId() + "\'"; // NON-NLS - } - // Get pairs of (object ID, keyword list name) for all files in the list of files that have - // keyword list hits. - String selectQuery = "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS hash_set_name " + - "FROM blackboard_artifacts " + - "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id " + - "WHERE blackboard_attributes.artifact_type_id=\'" + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + "\' " + - "AND blackboard_attributes.attribute_type_id=\'" + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "\' " + - "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + // hash set hits. + String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); SetHashSetNamesCallback callback = new SetHashSetNamesCallback(files); try { @@ -797,7 +780,7 @@ class FileSearch { /** * Create the callback. * - * @param resultFiles List of files to add keyword list names to + * @param resultFiles List of files to add hash set names to */ SetHashSetNamesCallback(List resultFiles) { this.resultFiles = resultFiles; @@ -815,12 +798,12 @@ class FileSearch { while (rs.next()) { try { Long objId = rs.getLong("object_id"); // NON-NLS - String hashSetName = rs.getString("hash_set_name"); // NON-NLS + String hashSetName = rs.getString("set_name"); // NON-NLS tempMap.get(objId).addHashSetName(hashSetName); } catch (SQLException ex) { - logger.log(Level.SEVERE, "Unable to get object_id or hash_set_name from result set", ex); // NON-NLS + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS } } } catch (SQLException ex) { @@ -841,7 +824,7 @@ class FileSearch { "FileSearch.HashHitsGroupKey.noHashHits=None", }) HashHitsGroupKey(ResultFile file) { - hashSetNames = file.getKeywordListNames(); + hashSetNames = file.getHashSetNames(); if (hashSetNames.isEmpty()) { hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits(); @@ -896,7 +879,378 @@ class FileSearch { return Objects.hash(hashSetNamesString); } } + + /** + * Attribute for grouping/sorting by interesting item set lists + */ + static class InterestingItemAttribute extends AttributeType { + @Override + GroupKey getGroupKey(ResultFile file) { + return new InterestingItemGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // interesting file set hits. + String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); + + SetInterstingFileSetNamesCallback callback = new SetInterstingFileSetNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up interesting file set attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select query. Will add + * the interesting file set names to the list of ResultFile objects. + */ + private static class SetInterstingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add interesting file set names to + */ + SetInterstingFileSetNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getAbstractFile().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String setName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addInterestingSetName(setName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get interesting file set names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing a interesting item set group + */ + private static class InterestingItemGroupKey extends GroupKey { + private final List interestingItemSetNames; + private final String interestingItemSetNamesString; + + @NbBundle.Messages({ + "FileSearch.InterestingItemGroupKey.noSets=None", + }) + InterestingItemGroupKey(ResultFile file) { + interestingItemSetNames = file.getInterestingSetNames(); + + if (interestingItemSetNames.isEmpty()) { + interestingItemSetNamesString = Bundle.FileSearch_InterestingItemGroupKey_noSets(); + } else { + interestingItemSetNamesString = String.join(",", interestingItemSetNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return interestingItemSetNamesString; + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof InterestingItemGroupKey) { + InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey)otherGroupKey; + + // Put the empty list at the end + if (this.interestingItemSetNames.isEmpty()) { + if (otherInterestingItemGroupKey.interestingItemSetNames.isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherInterestingItemGroupKey.interestingItemSetNames.isEmpty()) { + return -1; + } + + return interestingItemSetNamesString.compareTo(otherInterestingItemGroupKey.interestingItemSetNamesString); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this){ + return true; + } + + if (!(otherKey instanceof InterestingItemGroupKey)) { + return false; + } + + InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey)otherKey; + return interestingItemSetNamesString.equals(otherInterestingItemGroupKey.interestingItemSetNamesString); + } + + @Override + public int hashCode() { + return Objects.hash(interestingItemSetNamesString); + } + } + + /** + * Attribute for grouping/sorting by objects detected + */ + static class ObjectDetectedAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new ObjectDetectedGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // objects detected + String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID()); + + SetObjectDetectedNamesCallback callback = new SetObjectDetectedNamesCallback(files); + try { + caseDb.getCaseDbAccessManager().select(selectQuery, callback); + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up object detected attributes", ex); // NON-NLS + } + } + + /** + * Callback to process the results of the CaseDbAccessManager select query. Will add + * the interesting file set names to the list of ResultFile objects. + */ + private static class SetObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + List resultFiles; + + /** + * Create the callback. + * + * @param resultFiles List of files to add object detected names to + */ + SetObjectDetectedNamesCallback(List resultFiles) { + this.resultFiles = resultFiles; + } + + @Override + public void process(ResultSet rs) { + try { + // Create a temporary map of object ID to ResultFile + Map tempMap = new HashMap<>(); + for (ResultFile file : resultFiles) { + tempMap.put(file.getAbstractFile().getId(), file); + } + + while (rs.next()) { + try { + Long objId = rs.getLong("object_id"); // NON-NLS + String setName = rs.getString("set_name"); // NON-NLS + + tempMap.get(objId).addObjectDetectedName(setName); + + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Failed to get object detected names", ex); // NON-NLS + } + } + } + } + + /** + * Key representing an object detected group + */ + private static class ObjectDetectedGroupKey extends GroupKey { + private final List objectDetectedNames; + private final String objectDetectedNamesString; + + @NbBundle.Messages({ + "FileSearch.ObjectDetectedGroupKey.noSets=None", + }) + ObjectDetectedGroupKey(ResultFile file) { + objectDetectedNames = file.getObjectDetectedNames(); + + if (objectDetectedNames.isEmpty()) { + objectDetectedNamesString = Bundle.FileSearch_ObjectDetectedGroupKey_noSets(); + } else { + objectDetectedNamesString = String.join(",", objectDetectedNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return objectDetectedNamesString; + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof ObjectDetectedGroupKey) { + ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey)otherGroupKey; + + // Put the empty list at the end + if (this.objectDetectedNames.isEmpty()) { + if (otherObjectDetectedGroupKey.objectDetectedNames.isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherObjectDetectedGroupKey.objectDetectedNames.isEmpty()) { + return -1; + } + + return objectDetectedNamesString.compareTo(otherObjectDetectedGroupKey.objectDetectedNamesString); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this){ + return true; + } + + if (!(otherKey instanceof ObjectDetectedGroupKey)) { + return false; + } + + ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey)otherKey; + return objectDetectedNamesString.equals(otherObjectDetectedGroupKey.objectDetectedNamesString); + } + + @Override + public int hashCode() { + return Objects.hash(objectDetectedNamesString); + } + } + + /** + * Attribute for grouping/sorting by tag name + */ + static class FileTagAttribute extends AttributeType { + + @Override + GroupKey getGroupKey(ResultFile file) { + return new FileTagGroupKey(file); + } + + @Override + void addAttributeToResultFiles(List files, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + + try { + for (ResultFile resultFile : files) { + List contentTags = caseDb.getContentTagsByContent(resultFile.getAbstractFile()); + + for (ContentTag tag : contentTags) { + resultFile.addTagName(tag.getName().getDisplayName()); + } + } + } catch (TskCoreException ex) { + throw new FileSearchException("Error looking up file tag attributes", ex); // NON-NLS + } + } + } + + /** + * Key representing a interesting item set group + */ + private static class FileTagGroupKey extends GroupKey { + private final List tagNames; + private final String tagNamesString; + + @NbBundle.Messages({ + "FileSearch.FileTagGroupKey.noSets=None", + }) + FileTagGroupKey(ResultFile file) { + tagNames = file.getTagNames(); + + if (tagNames.isEmpty()) { + tagNamesString = Bundle.FileSearch_FileTagGroupKey_noSets(); + } else { + tagNamesString = String.join(",", tagNames); // NON-NLS + } + } + + @Override + String getDisplayName() { + return tagNamesString; + } + + @Override + public int compareTo(GroupKey otherGroupKey) { + if (otherGroupKey instanceof FileTagGroupKey) { + FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey)otherGroupKey; + + // Put the empty list at the end + if (tagNames.isEmpty()) { + if (otherFileTagGroupKey.tagNames.isEmpty()) { + return 0; + } else { + return 1; + } + } else if (otherFileTagGroupKey.tagNames.isEmpty()) { + return -1; + } + + return tagNamesString.compareTo(otherFileTagGroupKey.tagNamesString); + } else { + return compareClassNames(otherGroupKey); + } + } + + @Override + public boolean equals(Object otherKey) { + if (otherKey == this){ + return true; + } + + if (!(otherKey instanceof FileTagGroupKey)) { + return false; + } + + FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey)otherKey; + return tagNamesString.equals(otherFileTagGroupKey.tagNamesString); + } + + @Override + public int hashCode() { + return Objects.hash(tagNamesString); + } + } /** * Default attribute used to make one group @@ -956,6 +1310,28 @@ class FileSearch { } } + private static String createSetNameQuery(List files, SleuthkitCase caseDb, + int artifactTypeID, int setNameAttrID) throws FileSearchException { + + // Concatenate the object IDs in the list of files + String objIdList = ""; // NON-NLS + for (ResultFile file : files) { + if ( ! objIdList.isEmpty()) { + objIdList += ","; // NON-NLS + } + objIdList += "\'" + file.getAbstractFile().getId() + "\'"; // NON-NLS + } + + // Get pairs of (object ID, set name) for all files in the list of files that have + // the given artifact type. + return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name " + + "FROM blackboard_artifacts " + + "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id " + + "WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID + "\' " + + "AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID + "\' " + + "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + } + private FileSearch() { // Class should not be instantiated } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form index 057aaf9399..ad4d3d8015 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form @@ -66,37 +66,62 @@ - + + - + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + - @@ -109,8 +134,29 @@ - - + + + + + + + + + + + + + + + + + + + + + + + @@ -123,16 +169,16 @@ + + + + + - - - - - - - + + + - @@ -172,8 +218,8 @@ - + @@ -534,5 +580,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index e0d3e37ecc..884afa96f4 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.DefaultListModel; +import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JList; @@ -46,6 +47,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TagName; /** * Dialog to allow the user to choose filtering and grouping options. @@ -98,6 +100,11 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe setUpKWFilter(); setUpParentPathFilter(); + setUpHashFilter(); + setUpInterestingItemsFilter(); + setUpTagsFilter(); + setUpObjectFilter(); + // Set up the grouping attributes for (GroupingAttributeType type : GroupingAttributeType.values()) { groupComboBox.addItem(type); @@ -202,20 +209,8 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe try { DefaultListModel kwListModel = (DefaultListModel)kwList.getModel(); - // TODO - create case DB query - List arts = caseDb.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); - List setNames = new ArrayList<>(); - for (BlackboardArtifact art : arts) { - for (BlackboardAttribute attr : art.getAttributes()) { - if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { - String setName = attr.getValueString(); - if ( ! setNames.contains(setName)) { - setNames.add(setName); - } - } - } - } - Collections.sort(setNames); + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); for(String name : setNames) { kwListModel.add(count, name); } @@ -227,6 +222,116 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe addListeners(kwCheckBox, kwList); } + private void setUpHashFilter() { + int count = 0; + try { + DefaultListModel hashListModel = (DefaultListModel)hashList.getModel(); + + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); + for(String name : setNames) { + hashListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading hash set names", ex); + hashCheckBox.setEnabled(false); + hashList.setEnabled(false); + } + addListeners(hashCheckBox, hashList); + } + + private void setUpInterestingItemsFilter() { + int count = 0; + try { + DefaultListModel intListModel = (DefaultListModel)intList.getModel(); + + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); + for(String name : setNames) { + intListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading interesting file set names", ex); + intCheckBox.setEnabled(false); + intList.setEnabled(false); + } + addListeners(intCheckBox, intList); + } + + private void setUpTagsFilter() { + int count = 0; + try { + DefaultListModel tagsListModel = (DefaultListModel)tagsList.getModel(); + + List tagNames = caseDb.getTagNamesInUse(); + for(TagName name : tagNames) { + //tagsListModel.add(count, name.getDisplayName()); + tagsListModel.add(count, name); + count++; + } + tagsList.setCellRenderer(new TagsListCellRenderer()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading tag names", ex); + tagsCheckBox.setEnabled(false); + tagsList.setEnabled(false); + } + addListeners(tagsCheckBox, tagsList); + } + + private class TagsListCellRenderer extends DefaultListCellRenderer { + + @Override + public java.awt.Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + if (value instanceof TagName) { + value = ((TagName)value).getDisplayName(); + } + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + return this; + } + } + + private void setUpObjectFilter() { + int count = 0; + try { + DefaultListModel objListModel = (DefaultListModel)objList.getModel(); + + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION); + for(String name : setNames) { + objListModel.add(count, name); + count++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading object detected set names", ex); + objCheckBox.setEnabled(false); + objList.setEnabled(false); + } + addListeners(objCheckBox, objList); + } + + private List getSetNames(BlackboardArtifact.ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE setNameAttribute) throws TskCoreException { + List arts = caseDb.getBlackboardArtifacts(artifactType); + List setNames = new ArrayList<>(); + for (BlackboardArtifact art : arts) { + for (BlackboardAttribute attr : art.getAttributes()) { + if (attr.getAttributeType().getTypeID() == setNameAttribute.getTypeID()) { + String setName = attr.getValueString(); + if ( ! setNames.contains(setName)) { + setNames.add(setName); + } + } + } + } + Collections.sort(setNames); + return setNames; + } + /** * Initialize the parent path filter */ @@ -272,6 +377,22 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe filters.add(new FileSearchFiltering.KeywordListFilter(kwList.getSelectedValuesList())); } + if (hashCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.HashSetFilter(hashList.getSelectedValuesList())); + } + + if (intCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.InterestingFileSetFilter(intList.getSelectedValuesList())); + } + + if (objCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.ObjectDetectionFilter(objList.getSelectedValuesList())); + } + + if (tagsCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.TagsFilter(tagsList.getSelectedValuesList())); + } + return filters; } @@ -385,6 +506,26 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe return; } + if (hashCheckBox.isSelected() && hashList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one hash set name must be selected"); + return; + } + + if (intCheckBox.isSelected() && intList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one interesting file set name must be selected"); + return; + } + + if (objCheckBox.isSelected() && objList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one object type name must be selected"); + return; + } + + if (tagsCheckBox.isSelected() && tagsList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one tag name must be selected"); + return; + } + setValid(); } @@ -456,6 +597,18 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe errorLabel = new javax.swing.JLabel(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(8, 7), new java.awt.Dimension(8, 7), new java.awt.Dimension(8, 7)); filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(265, 23), new java.awt.Dimension(265, 23), new java.awt.Dimension(265, 23)); + hashCheckBox = new javax.swing.JCheckBox(); + jScrollPane7 = new javax.swing.JScrollPane(); + hashList = new javax.swing.JList<>(); + intCheckBox = new javax.swing.JCheckBox(); + jScrollPane8 = new javax.swing.JScrollPane(); + intList = new javax.swing.JList<>(); + jScrollPane9 = new javax.swing.JScrollPane(); + tagsList = new javax.swing.JList<>(); + tagsCheckBox = new javax.swing.JCheckBox(); + jScrollPane10 = new javax.swing.JScrollPane(); + objList = new javax.swing.JList<>(); + objCheckBox = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -580,6 +733,50 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe errorLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.errorLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.hashCheckBox.text")); // NOI18N + hashCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hashCheckBoxActionPerformed(evt); + } + }); + + hashList.setModel(new DefaultListModel()); + hashList.setEnabled(false); + jScrollPane7.setViewportView(hashList); + + org.openide.awt.Mnemonics.setLocalizedText(intCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.intCheckBox.text")); // NOI18N + intCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + intCheckBoxActionPerformed(evt); + } + }); + + intList.setModel(new DefaultListModel()); + intList.setEnabled(false); + jScrollPane8.setViewportView(intList); + + tagsList.setModel(new DefaultListModel()); + tagsList.setEnabled(false); + jScrollPane9.setViewportView(tagsList); + + org.openide.awt.Mnemonics.setLocalizedText(tagsCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.tagsCheckBox.text")); // NOI18N + tagsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tagsCheckBoxActionPerformed(evt); + } + }); + + objList.setModel(new DefaultListModel()); + objList.setEnabled(false); + jScrollPane10.setViewportView(objList); + + org.openide.awt.Mnemonics.setLocalizedText(objCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.objCheckBox.text")); // NOI18N + objCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + objCheckBoxActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -615,30 +812,50 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(jScrollPane5) .addComponent(jScrollPane6, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton)) - .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(cancelButton) + .addContainerGap()) .addGroup(layout.createSequentialGroup() + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(344, 344, 344)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel3) - .addComponent(jLabel4) - .addComponent(jLabel5)) - .addGap(29, 29, 29) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(orderAttrRadioButton) - .addComponent(groupComboBox, 0, 144, Short.MAX_VALUE) - .addComponent(orderSizeRadioButton) - .addComponent(fileOrderComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 25, Short.MAX_VALUE))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(hashCheckBox) + .addGap(18, 18, 18) + .addComponent(jScrollPane7)) + .addGroup(layout.createSequentialGroup() + .addComponent(intCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane8)) + .addGroup(layout.createSequentialGroup() + .addComponent(tagsCheckBox) + .addGap(18, 18, 18) + .addComponent(jScrollPane9)) + .addGroup(layout.createSequentialGroup() + .addComponent(objCheckBox) + .addGap(18, 18, 18) + .addComponent(jScrollPane10))) + .addGap(35, 35, 35) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) + .addComponent(jLabel4) + .addComponent(jLabel5)) + .addGap(29, 29, 29) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(orderAttrRadioButton) + .addComponent(groupComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(orderSizeRadioButton) + .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -648,8 +865,24 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel1) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jScrollPane7, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(hashCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane10, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(objCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane9, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tagsCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(dsCheckBox) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel3) @@ -659,14 +892,14 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(jLabel4) .addComponent(orderAttrRadioButton)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(orderSizeRadioButton))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(dsCheckBox) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel5) - .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(orderSizeRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane8, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(intCheckBox))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(freqCheckBox) @@ -698,8 +931,8 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(errorLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(cancelButton) - .addComponent(searchButton)) + .addComponent(searchButton) + .addComponent(cancelButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); @@ -767,6 +1000,22 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe setVisible(false); }//GEN-LAST:event_searchButtonActionPerformed + private void hashCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashCheckBoxActionPerformed + hashList.setEnabled(hashCheckBox.isSelected()); + }//GEN-LAST:event_hashCheckBoxActionPerformed + + private void intCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intCheckBoxActionPerformed + intList.setEnabled(intCheckBox.isSelected()); + }//GEN-LAST:event_intCheckBoxActionPerformed + + private void tagsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tagsCheckBoxActionPerformed + tagsList.setEnabled(tagsCheckBox.isSelected()); + }//GEN-LAST:event_tagsCheckBoxActionPerformed + + private void objCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_objCheckBoxActionPerformed + objList.setEnabled(objCheckBox.isSelected()); + }//GEN-LAST:event_objCheckBoxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton addParentButton; private javax.swing.JButton cancelButton; @@ -781,19 +1030,29 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JCheckBox freqCheckBox; private javax.swing.JList freqList; private javax.swing.JComboBox groupComboBox; + private javax.swing.JCheckBox hashCheckBox; + private javax.swing.JList hashList; + private javax.swing.JCheckBox intCheckBox; + private javax.swing.JList intList; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane10; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; private javax.swing.JScrollPane jScrollPane4; private javax.swing.JScrollPane jScrollPane5; private javax.swing.JScrollPane jScrollPane6; + private javax.swing.JScrollPane jScrollPane7; + private javax.swing.JScrollPane jScrollPane8; + private javax.swing.JScrollPane jScrollPane9; private javax.swing.JCheckBox kwCheckBox; private javax.swing.JList kwList; + private javax.swing.JCheckBox objCheckBox; + private javax.swing.JList objList; private javax.swing.JRadioButton orderAttrRadioButton; private javax.swing.ButtonGroup orderButtonGroup; private javax.swing.JRadioButton orderSizeRadioButton; @@ -806,5 +1065,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JButton searchButton; private javax.swing.JCheckBox sizeCheckBox; private javax.swing.JList sizeList; + private javax.swing.JCheckBox tagsCheckBox; + private javax.swing.JList tagsList; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index d30b04279c..42d3993518 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -30,12 +30,15 @@ import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; /** * Run various filters to return a subset of files from the current case. @@ -381,13 +384,7 @@ class FileSearchFiltering { @Override String getWhereClause() { - String keywordListPart = ""; // NON-NLS - for (String listName : listNames) { - if (! keywordListPart.isEmpty()) { - keywordListPart += " OR "; // NON-NLS - } - keywordListPart += "value_text = \'" + listName + "\'"; // TODO - these should really be prepared statements // NON-NLS - } + String keywordListPart = concatenateNamesForSQL(listNames); String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = 9 AND attribute_type_ID = 37 " + @@ -399,19 +396,10 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}", - "FileSearchFiltering.KeywordListFilter.comma=, ", }) @Override String getDesc() { - String desc = ""; // NON-NLS - for (String listName : listNames) { - if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_KeywordListFilter_comma(); - } - desc += listName; - } - desc = Bundle.FileSearchFiltering_KeywordListFilter_desc(desc); - return desc; + return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames)); } } @@ -543,6 +531,198 @@ class FileSearchFiltering { } } + /** + * A filter for specifying hash set names. + * A file must match one of the given sets to pass. + */ + static class HashSetFilter extends FileFilter { + private final List setNames; + + /** + * Create the HashSetFilter + * @param setNames + */ + HashSetFilter(List setNames) { + this.setNames = setNames; + } + + @Override + String getWhereClause() { + String hashSetPart = concatenateNamesForSQL(setNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " " + + "AND (" + hashSetPart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0}", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames)); + } + } + + /** + * A filter for specifying interesting file set names. + * A file must match one of the given sets to pass. + */ + static class InterestingFileSetFilter extends FileFilter { + private final List setNames; + + /** + * Create the InterestingFileSetFilter + * @param setNames + */ + InterestingFileSetFilter(List setNames) { + this.setNames = setNames; + } + + @Override + String getWhereClause() { + String intItemSetPart = concatenateNamesForSQL(setNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " " + + "AND (" + intItemSetPart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0}", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames)); + } + } + + /** + * A filter for specifying object types detected. + * A file must match one of the given types to pass. + */ + static class ObjectDetectionFilter extends FileFilter { + private final List typeNames; + + /** + * Create the ObjectDetectionFilter + * @param typeNames + */ + ObjectDetectionFilter(List typeNames) { + this.typeNames = typeNames; + } + + @Override + String getWhereClause() { + String objTypePart = concatenateNamesForSQL(typeNames); + + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() + + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " " + + "AND (" + objTypePart + "))))"; // NON-NLS + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0}", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames)); + } + } + + /** + * A filter for specifying tag names. + * A file must contain one of the given tags to pass. + */ + static class TagsFilter extends FileFilter { + private final List tagNames; + + /** + * Create the TagsFilter + * @param tagNames + */ + TagsFilter(List tagNames) { + this.tagNames = tagNames; + } + + @Override + String getWhereClause() { + String tagIDs = ""; // NON-NLS + for (TagName tagName : tagNames) { + if (! tagIDs.isEmpty()) { + tagIDs += ","; + } + tagIDs += tagName.getId(); + } + + String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))"; + + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - tag names", + "FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0}", + "FileSearchFiltering.TagsFilter.or= or ", + }) + @Override + String getDesc() { + String desc = ""; // NON-NLS + for (TagName name : tagNames) { + if ( ! desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_TagsFilter_or(); + } + desc += name.getDisplayName(); + } + return Bundle.FileSearchFiltering_TagsFilter_desc(desc); // Nope + } + } + + @NbBundle.Messages({ + "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ", + }) + private static String concatenateSetNamesForDisplay(List setNames) { + String desc = ""; // NON-NLS + for (String setName : setNames) { + if ( ! desc.isEmpty()) { + desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma(); + } + desc += setName; + } + return desc; + } + + /** + * Concatenate the set names into an "OR" separated list. + * This does not do any SQL-escaping. + * + * @param setNames + * + * @return the list to use in the SQL query + */ + private static String concatenateNamesForSQL(List setNames) { + String result = ""; // NON-NLS + for (String setName : setNames) { + if (! result.isEmpty()) { + result += " OR "; // NON-NLS + } + result += "value_text = \'" + setName + "\'"; // NON-NLS + } + return result; + } + private FileSearchFiltering() { // Class should not be instantiated } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java index f5b71c7016..8d6c176d01 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -36,6 +36,7 @@ class ResultFile { private final List hashSetNames; private final List tagNames; private final List interestingSetNames; + private final List objectDetectedNames; private FileType fileType; /** @@ -47,9 +48,11 @@ class ResultFile { this.abstractFile = abstractFile; this.frequency = FileSearchData.Frequency.UNKNOWN; keywordListNames = new ArrayList<>(); - hashSetNames = new ArrayList(); - tagNames = new ArrayList(); - interestingSetNames = new ArrayList(); + hashSetNames = new ArrayList<>(); + tagNames = new ArrayList<>(); + interestingSetNames = new ArrayList<>(); + objectDetectedNames = new ArrayList<>(); + fileType = FileType.OTHER; } @@ -159,7 +162,7 @@ class ResultFile { } /** - * Add a hash set name that matched this file. + * Add an interesting file set name that matched this file. * * @param interestingSetName */ @@ -181,6 +184,29 @@ class ResultFile { return interestingSetNames; } + /** + * Add an object detected in this file. + * + * @param objectDetectedName + */ + void addObjectDetectedName (String objectDetectedName) { + if (! objectDetectedNames.contains(objectDetectedName)) { + objectDetectedNames.add(objectDetectedName); + } + + // Sort the list so the getObjectDetectedNames() will be consistent regardless of the order added + Collections.sort(objectDetectedNames); + } + + /** + * Get the objects detected for this file + * + * @return the objects detected in this file. + */ + List getObjectDetectedNames() { + return objectDetectedNames; + } + /** * Get the AbstractFile * From 89c059b6c3a0a6648f3daed84bd9924789594889 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 10 Jul 2019 13:04:01 -0400 Subject: [PATCH 03/11] Adding checkboxes for last three filters --- .../autopsy/filequery/Bundle.properties | 3 + .../filequery/Bundle.properties-MERGED | 3 + .../autopsy/filequery/FileSearchDialog.form | 64 +++++++++++++------ .../autopsy/filequery/FileSearchDialog.java | 62 +++++++++++------- 4 files changed, 89 insertions(+), 43 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index c330714cab..20cd4e3d05 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -24,3 +24,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects +FileSearchDialog.jCheckBox1.text=Must contain EXIF data +FileSearchDialog.jCheckBox2.text=Must have been previously tagged as notable +FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index a88262896f..d5cc122888 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -110,3 +110,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects +FileSearchDialog.jCheckBox1.text=Must contain EXIF data +FileSearchDialog.jCheckBox2.text=Must have been previously tagged as notable +FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form index ad4d3d8015..c60b5db83f 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form @@ -75,10 +75,6 @@ - - - - @@ -121,6 +117,15 @@ + + + + + + + + + @@ -141,20 +146,12 @@ - - - - - - - - - - + + @@ -175,25 +172,30 @@ - - - - - + + + + + - + + + + + + @@ -203,6 +205,7 @@ + @@ -700,5 +703,26 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index 884afa96f4..f0403442da 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -609,6 +609,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe jScrollPane10 = new javax.swing.JScrollPane(); objList = new javax.swing.JList<>(); objCheckBox = new javax.swing.JCheckBox(); + jCheckBox1 = new javax.swing.JCheckBox(); + jCheckBox2 = new javax.swing.JCheckBox(); + jCheckBox3 = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -777,6 +780,12 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe } }); + org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jCheckBox2, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox2.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jCheckBox3, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox3.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -820,9 +829,6 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(344, 344, 344)) @@ -855,7 +861,14 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(groupComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(orderSizeRadioButton) .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()))) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jCheckBox1) + .addComponent(jCheckBox2) + .addComponent(jCheckBox3)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -871,18 +884,12 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane7, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(hashCheckBox)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane10, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(objCheckBox)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane9, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(tagsCheckBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(dsCheckBox) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jScrollPane10, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(objCheckBox))) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel3) @@ -896,29 +903,35 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel5) - .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane8, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(intCheckBox))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGap(11, 11, 11) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(freqCheckBox) - .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jScrollPane9, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tagsCheckBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(sizeCheckBox)) + .addComponent(sizeCheckBox) + .addComponent(jScrollPane8, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(intCheckBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(jScrollPane5, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(kwCheckBox)) + .addComponent(kwCheckBox) + .addGroup(layout.createSequentialGroup() + .addComponent(jCheckBox1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jCheckBox2))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(parentCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel2)) - .addComponent(jScrollPane6, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane6, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jCheckBox3)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(deleteParentButton) @@ -1034,6 +1047,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JList hashList; private javax.swing.JCheckBox intCheckBox; private javax.swing.JList intList; + private javax.swing.JCheckBox jCheckBox1; + private javax.swing.JCheckBox jCheckBox2; + private javax.swing.JCheckBox jCheckBox3; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; From e33880f8a30c70f5c24e3fd98b053f39e03e21c0 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 11 Jul 2019 15:06:03 -0400 Subject: [PATCH 04/11] Added EXIF and notable filters. Added paging support. --- .../autopsy/filequery/Bundle.properties | 4 +- .../filequery/Bundle.properties-MERGED | 6 +- .../autopsy/filequery/FileGroup.java | 4 +- .../autopsy/filequery/FileSearch.java | 102 +++++++++++++++++- .../autopsy/filequery/FileSearchDialog.form | 19 ++-- .../autopsy/filequery/FileSearchDialog.java | 37 +++++-- .../filequery/FileSearchFiltering.java | 99 ++++++++++++++++- .../filequery/FileSearchTestAction.java | 55 ++++++++-- 8 files changed, 288 insertions(+), 38 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 20cd4e3d05..27863cffee 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -24,6 +24,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects -FileSearchDialog.jCheckBox1.text=Must contain EXIF data -FileSearchDialog.jCheckBox2.text=Must have been previously tagged as notable FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria +FileSearchDialog.exifCheckBox.text=Must contain EXIF data +FileSearchDialog.notableCheckBox.text=Must have been tagged as notable diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index d5cc122888..378d27edd1 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -63,6 +63,7 @@ FileSearchFiltering.DataSourceFilter.datasource={0}({1}) # {0} - filters FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0} FileSearchFiltering.DataSourceFilter.or=\ or +FileSearchFiltering.ExifFilter.desc=Files that contain EXIF data # {0} - filters FileSearchFiltering.FileTypeFilter.desc=Files with type: {0} FileSearchFiltering.FileTypeFilter.or=\ or @@ -86,6 +87,7 @@ FileSearchFiltering.ParentFilter.substring=(substring) FileSearchFiltering.ParentSearchTerm.fullString=\ {0} (exact) # {0} - search term FileSearchFiltering.ParentSearchTerm.subString=\ {0} (substring) +FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable # {0} - filters FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} FileSearchFiltering.SizeFilter.or=\ or @@ -110,6 +112,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects -FileSearchDialog.jCheckBox1.text=Must contain EXIF data -FileSearchDialog.jCheckBox2.text=Must have been previously tagged as notable FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria +FileSearchDialog.exifCheckBox.text=Must contain EXIF data +FileSearchDialog.notableCheckBox.text=Must have been tagged as notable diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java index 26b9b1f552..6d0ecb936c 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java @@ -59,13 +59,13 @@ class FileGroup implements Comparable { } /** - * Get the display name for this group, including the size of the group. + * Get the display name for this group. * This must be unique for each group. * * @return the display name */ String getDisplayName() { - return displayName + " (" + files.size() + ")"; // NON-NLS + return displayName; // NON-NLS } /** diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index 2c917de708..a8f8904f0c 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.filequery; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -60,7 +61,6 @@ class FileSearch { * @param groupAttributeType The attribute to use for grouping * @param groupSortingType The method to use to sort the groups * @param fileSortingMethod The method to use to sort the files within the groups - * @param attributesNeededForGroupingOrSorting Any attributes that will used for grouping or sorting * @param caseDb The case database * @param centralRepoDb The central repository database. Can be null if not needed. * @@ -73,9 +73,16 @@ class FileSearch { AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, FileSorter.SortingMethod fileSortingMethod, - List attributesNeededForGroupingOrSorting, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + // Make a list of attributes that we want to add values for. This ensures the + // ResultFile objects will have all needed fields set when it's time to group + // and sort them. For example, if we're grouping by central repo frequency, we need + // to make sure we've loaded those values before grouping. + List attributesNeededForGroupingOrSorting = new ArrayList<>(); + attributesNeededForGroupingOrSorting.add(groupAttributeType); + attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes()); + // Run the queries for each filter List resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb); @@ -92,6 +99,87 @@ class FileSearch { return searchResults; } + /** + * Run the file search to get the group names and sizes. + * + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the groups + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if not needed. + * + * @return A LinkedHashMap grouped and sorted according to the parameters + * + * @throws FileSearchException + */ + static LinkedHashMap getGroupSizes( + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + + LinkedHashMap> searchResults = runFileSearch(filters, + groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); + + LinkedHashMap groupSizes = new LinkedHashMap<>(); + for (String groupName : searchResults.keySet()) { + groupSizes.put(groupName, searchResults.get(groupName).size()); + } + return groupSizes; + } + + /** + * Run the file search to get the group names and sizes. + * + * @param filters The filters to apply + * @param groupAttributeType The attribute to use for grouping + * @param groupSortingType The method to use to sort the groups + * @param fileSortingMethod The method to use to sort the files within the groups + * @param groupName Name of the group to get entries from + * @param startingEntry The first entry to return + * @param numberOfEntries The number of entries to return + * @param caseDb The case database + * @param centralRepoDb The central repository database. Can be null if not needed. + * + * @return A LinkedHashMap grouped and sorted according to the parameters + * + * @throws FileSearchException + */ + static List getGroupEntries( + List filters, + AttributeType groupAttributeType, + FileGroup.GroupSortingAlgorithm groupSortingType, + FileSorter.SortingMethod fileSortingMethod, + String groupName, + int startingEntry, + int numberOfEntries, + SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + + LinkedHashMap> searchResults = runFileSearch(filters, + groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); + + List page = new ArrayList<>(); + + // Check that the group exists + if (! searchResults.containsKey(groupName)) { + return page; + } + + // Check that there is data after the starting point + if (searchResults.get(groupName).size() < startingEntry) { + return page; + } + + // Add each page in the range + for (int i = startingEntry; (i < startingEntry + numberOfEntries) + && (i < searchResults.get(groupName).size());i++) { + page.add(searchResults.get(groupName).get(i)); + } + return page; + } + /** * Run the file search. * @@ -99,7 +187,6 @@ class FileSearch { * @param groupAttributeType The attribute to use for grouping * @param groupSortingType The method to use to sort the groups * @param fileSortingMethod The method to use to sort the files within the groups - * @param attributesNeededForGroupingOrSorting Any attributes that will used for grouping or sorting * @param caseDb The case database * @param centralRepoDb The central repository database. Can be null if not needed. * @@ -112,9 +199,16 @@ class FileSearch { AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, FileSorter.SortingMethod fileSortingMethod, - List attributesNeededForGroupingOrSorting, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { + // Make a list of attributes that we want to add values for. This ensures the + // ResultFile objects will have all needed fields set when it's time to group + // and sort them. For example, if we're grouping by central repo frequency, we need + // to make sure we've loaded those values before grouping. + List attributesNeededForGroupingOrSorting = new ArrayList<>(); + attributesNeededForGroupingOrSorting.add(groupAttributeType); + attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes()); + // Run the queries for each filter List resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb); diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form index c60b5db83f..ab99037e47 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form @@ -120,8 +120,8 @@ - - + + @@ -192,9 +192,9 @@ - + - + @@ -703,19 +703,22 @@ - + - + - + - + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index f0403442da..2a7e448c2c 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -393,6 +393,14 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe filters.add(new FileSearchFiltering.TagsFilter(tagsList.getSelectedValuesList())); } + if (exifCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.ExifFilter()); + } + + if (notableCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.NotableFilter()); + } + return filters; } @@ -609,8 +617,8 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe jScrollPane10 = new javax.swing.JScrollPane(); objList = new javax.swing.JList<>(); objCheckBox = new javax.swing.JCheckBox(); - jCheckBox1 = new javax.swing.JCheckBox(); - jCheckBox2 = new javax.swing.JCheckBox(); + exifCheckBox = new javax.swing.JCheckBox(); + notableCheckBox = new javax.swing.JCheckBox(); jCheckBox3 = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -780,9 +788,14 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe } }); - org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox1.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(exifCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.exifCheckBox.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jCheckBox2, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox2.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(notableCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.notableCheckBox.text")); // NOI18N + notableCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + notableCheckBoxActionPerformed(evt); + } + }); org.openide.awt.Mnemonics.setLocalizedText(jCheckBox3, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox3.text")); // NOI18N @@ -865,8 +878,8 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jCheckBox1) - .addComponent(jCheckBox2) + .addComponent(exifCheckBox) + .addComponent(notableCheckBox) .addComponent(jCheckBox3)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); @@ -921,9 +934,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(jScrollPane5, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(kwCheckBox) .addGroup(layout.createSequentialGroup() - .addComponent(jCheckBox1) + .addComponent(exifCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jCheckBox2))) + .addComponent(notableCheckBox))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -1029,6 +1042,10 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe objList.setEnabled(objCheckBox.isSelected()); }//GEN-LAST:event_objCheckBoxActionPerformed + private void notableCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_notableCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_notableCheckBoxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton addParentButton; private javax.swing.JButton cancelButton; @@ -1036,6 +1053,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JCheckBox dsCheckBox; private javax.swing.JList dsList; private javax.swing.JLabel errorLabel; + private javax.swing.JCheckBox exifCheckBox; private javax.swing.JComboBox fileOrderComboBox; private javax.swing.JList fileTypeList; private javax.swing.Box.Filler filler1; @@ -1047,8 +1065,6 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JList hashList; private javax.swing.JCheckBox intCheckBox; private javax.swing.JList intList; - private javax.swing.JCheckBox jCheckBox1; - private javax.swing.JCheckBox jCheckBox2; private javax.swing.JCheckBox jCheckBox3; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; @@ -1067,6 +1083,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JScrollPane jScrollPane9; private javax.swing.JCheckBox kwCheckBox; private javax.swing.JList kwList; + private javax.swing.JCheckBox notableCheckBox; private javax.swing.JCheckBox objCheckBox; private javax.swing.JList objList; private javax.swing.JRadioButton orderAttrRadioButton; diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index 42d3993518..10b7574d08 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -686,10 +686,107 @@ class FileSearchFiltering { } desc += name.getDisplayName(); } - return Bundle.FileSearchFiltering_TagsFilter_desc(desc); // Nope + return Bundle.FileSearchFiltering_TagsFilter_desc(desc); } } + /** + * A filter for specifying that the file must have EXIF data. + */ + static class ExifFilter extends FileFilter { + + /** + * Create the ExifFilter + */ + ExifFilter() { + } + + @Override + String getWhereClause() { + String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN " + + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + + BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID() + ")))"; + + return queryStr; + } + + @NbBundle.Messages({ + "FileSearchFiltering.ExifFilter.desc=Files that contain EXIF data", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ExifFilter_desc(); + } + } + + /** + * A filter for specifying that the file must have been marked as notable in the CR. + */ + static class NotableFilter extends FileFilter { + + /** + * Create the NotableFilter + */ + NotableFilter() { + } + + @Override + String getWhereClause() { + // Since this relies on the central repository database, there is no + // query on the case database. + return ""; // NON-NLS + } + + @Override + boolean useAlternateFilter() { + return true; + } + + @Override + List applyAlternateFilter (List currentResults, SleuthkitCase caseDb, + EamDb centralRepoDb) throws FileSearchException { + + if (centralRepoDb == null) { + throw new FileSearchException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS + } + + // We have to have run some kind of SQL filter before getting to this point, + // and should have checked afterward to see if the results were empty. + if (currentResults.isEmpty()) { + throw new FileSearchException("Can not run on empty list"); // NON-NLS + } + + // The matching files + List notableResults = new ArrayList<>(); + + try { + CorrelationAttributeInstance.Type type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); + + for (ResultFile file : currentResults) { + if (file.getAbstractFile().getMd5Hash() != null && ! file.getAbstractFile().getMd5Hash().isEmpty()) { + + // Check if this file hash is marked as notable in the CR + String value = file.getAbstractFile().getMd5Hash(); + if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) { + notableResults.add(file); + } + } + } + return notableResults; + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { + throw new FileSearchException("Error querying central repository", ex); // NON-NLS + } + } + + @NbBundle.Messages({ + "FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); + } + } + @NbBundle.Messages({ "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ", }) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java index b1481dd441..90fbcafa47 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.filequery; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.logging.Level; import org.apache.commons.lang.exception.ExceptionUtils; @@ -32,6 +33,7 @@ import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; /** * Class to test the file search API. Allows the user to run searches and see results. @@ -88,21 +90,56 @@ public final class FileSearchTestAction extends CallableSystemAction { FileSorter.SortingMethod fileSort = dialog.getFileSortingMethod(); try { - - // Make a list of attributes that we want to add values for. This ensures the - // ResultFile objects will have all needed fields set when it's time to group - // and sort them. For example, if we're grouping by central repo frequency, we need - // to make sure we've loaded those values before grouping. - List attrsForGroupingAndSorting = new ArrayList<>(); - attrsForGroupingAndSorting.add(groupingAttr); - attrsForGroupingAndSorting.addAll(fileSort.getRequiredAttributes()); + + // Test getting the groups + LinkedHashMap groups = FileSearch.getGroupSizes(filters, + groupingAttr, + groupSortAlgorithm, + fileSort, + Case.getCurrentCase().getSleuthkitCase(), crDb); + + System.out.println("Groups: "); + for (String name : groups.keySet()) { + System.out.println(" " + name + " : " + groups.get(name)); + } + + if (groups.size() > 0) { + String firstGroupName = groups.keySet().iterator().next(); + List entries0to5 = FileSearch.getGroupEntries(filters, + groupingAttr, + groupSortAlgorithm, + fileSort, + firstGroupName, + 0, + 5, + Case.getCurrentCase().getSleuthkitCase(), crDb); + System.out.println("First five " + firstGroupName + " : "); + for (AbstractFile f : entries0to5) { + System.out.println(" " + f.getName()); + } + + List entries6to106 = FileSearch.getGroupEntries(filters, + groupingAttr, + groupSortAlgorithm, + fileSort, + firstGroupName, + 5, + 100, + Case.getCurrentCase().getSleuthkitCase(), crDb); + System.out.println(firstGroupName + " 6 to 106: "); + for (AbstractFile f : entries6to106) { + System.out.println(" " + f.getName()); + } + } + + + ///////////////// // Run the search SearchResults results = FileSearch.runFileSearchDebug(filters, groupingAttr, groupSortAlgorithm, fileSort, - attrsForGroupingAndSorting, Case.getCurrentCase().getSleuthkitCase(), crDb); // Display the results From 05f3d16c63af4ca800a54a28aea43f5debc3ef01 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Fri, 12 Jul 2019 07:45:17 -0400 Subject: [PATCH 05/11] Minor cleanup --- Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index 2a7e448c2c..cea446d7f1 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -267,7 +267,6 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe List tagNames = caseDb.getTagNamesInUse(); for(TagName name : tagNames) { - //tagsListModel.add(count, name.getDisplayName()); tagsListModel.add(count, name); count++; } From 2e582837823cfb69afe8bd10f91e0ab1204774da Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Mon, 15 Jul 2019 14:41:57 -0400 Subject: [PATCH 06/11] Added score filter --- .../autopsy/filequery/Bundle.properties | 2 +- .../filequery/Bundle.properties-MERGED | 7 +- .../autopsy/filequery/FileSearchData.java | 45 +++++++++ .../autopsy/filequery/FileSearchDialog.form | 82 +++++++++------- .../autopsy/filequery/FileSearchDialog.java | 97 +++++++++++++------ .../filequery/FileSearchFiltering.java | 80 +++++++++++++++ 6 files changed, 248 insertions(+), 65 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 27863cffee..970efb033a 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -24,6 +24,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects -FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria FileSearchDialog.exifCheckBox.text=Must contain EXIF data FileSearchDialog.notableCheckBox.text=Must have been tagged as notable +FileSearchDialog.scoreCheckBox.text=Has score diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 378d27edd1..260d33c651 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -36,6 +36,9 @@ FileSearchData.Frequency.common.displayName=Common FileSearchData.Frequency.rare.displayName=Rare FileSearchData.Frequency.unique.displayName=Unique FileSearchData.Frequency.unknown.displayName=Unknown +FileSearchData.Score.interesting.displayName=Interesting +FileSearchData.Score.notable.displayName=Notable +FileSearchData.Score.unknown.displayName=Unknown FileSearchDialog.dialogTitle.text=Test file search FileSearchDialog.jLabel1.text=File Type FileSearchDialog.dsCheckBox.text=Data source @@ -89,6 +92,8 @@ FileSearchFiltering.ParentSearchTerm.fullString=\ {0} (exact) FileSearchFiltering.ParentSearchTerm.subString=\ {0} (substring) FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable # {0} - filters +FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0} +# {0} - filters FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} FileSearchFiltering.SizeFilter.or=\ or # {0} - Minimum bytes @@ -112,6 +117,6 @@ FileSearchDialog.hashCheckBox.text=Hash Set FileSearchDialog.intCheckBox.text=Interesting Items FileSearchDialog.tagsCheckBox.text=Tags FileSearchDialog.objCheckBox.text=Objects -FileSearchDialog.jCheckBox3.text=Must meet "is suspicious" criteria FileSearchDialog.exifCheckBox.text=Must contain EXIF data FileSearchDialog.notableCheckBox.text=Must have been tagged as notable +FileSearchDialog.scoreCheckBox.text=Has score diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java index 6356dbd99d..00ff9ff8de 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java @@ -266,6 +266,51 @@ class FileSearchData { } } + /** + * Enum representing the score of the file. + */ + @NbBundle.Messages({ + "FileSearchData.Score.notable.displayName=Notable", + "FileSearchData.Score.interesting.displayName=Interesting", + "FileSearchData.Score.unknown.displayName=Unknown", + }) + enum Score { + NOTABLE(0, Bundle.FileSearchData_Score_notable_displayName()), + INTERESTING(1, Bundle.FileSearchData_Score_interesting_displayName()), + UNKNOWN(2, Bundle.FileSearchData_Score_unknown_displayName()); + + private final int ranking; + private final String displayName; + + Score(int ranking, String displayName) { + this.ranking = ranking; + this.displayName = displayName; + } + + /** + * Get the rank for sorting. + * + * @return the rank (lower should be displayed first) + */ + int getRanking() { + return ranking; + } + + /** + * Get the list of enums that are valid for filtering. + * + * @return enums that can be used to filter + */ + static List getOptionsForFiltering() { + return Arrays.asList(NOTABLE, INTERESTING); + } + + @Override + public String toString() { + return displayName; + } + } + private FileSearchData() { // Class should not be instantiated } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form index ab99037e47..f649f3c3c7 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.form @@ -81,26 +81,19 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -122,7 +115,6 @@ - @@ -188,16 +180,13 @@ - + - - - - - + + - + @@ -205,15 +194,19 @@ - + + + + + - + - + @@ -720,12 +713,35 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index cea446d7f1..a22e12aaa8 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.filequery.FileSearch.GroupingAttributeType; import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; +import org.sleuthkit.autopsy.filequery.FileSearchData.Score; import org.sleuthkit.autopsy.filequery.FileSearchFiltering.ParentSearchTerm; import org.sleuthkit.autopsy.filequery.FileSorter.SortingMethod; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -104,6 +105,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe setUpInterestingItemsFilter(); setUpTagsFilter(); setUpObjectFilter(); + setUpScoreFilter(); // Set up the grouping attributes for (GroupingAttributeType type : GroupingAttributeType.values()) { @@ -314,6 +316,19 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe addListeners(objCheckBox, objList); } + /** + * Initialize the score filter + */ + private void setUpScoreFilter() { + + int count = 0; + DefaultListModel scoreListModel = (DefaultListModel)scoreList.getModel(); + for (Score score : Score.getOptionsForFiltering()) { + scoreListModel.add(count, score); + } + addListeners(scoreCheckBox, scoreList); + } + private List getSetNames(BlackboardArtifact.ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE setNameAttribute) throws TskCoreException { List arts = caseDb.getBlackboardArtifacts(artifactType); List setNames = new ArrayList<>(); @@ -400,6 +415,10 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe filters.add(new FileSearchFiltering.NotableFilter()); } + if (scoreCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.ScoreFilter(scoreList.getSelectedValuesList())); + } + return filters; } @@ -533,6 +552,11 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe return; } + if (scoreCheckBox.isSelected() && scoreList.getSelectedValuesList().isEmpty()) { + setInvalid("At least one score must be selected"); + return; + } + setValid(); } @@ -618,7 +642,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe objCheckBox = new javax.swing.JCheckBox(); exifCheckBox = new javax.swing.JCheckBox(); notableCheckBox = new javax.swing.JCheckBox(); - jCheckBox3 = new javax.swing.JCheckBox(); + scoreCheckBox = new javax.swing.JCheckBox(); + jScrollPane11 = new javax.swing.JScrollPane(); + scoreList = new javax.swing.JList<>(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -796,7 +822,16 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe } }); - org.openide.awt.Mnemonics.setLocalizedText(jCheckBox3, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jCheckBox3.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(scoreCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.scoreCheckBox.text")); // NOI18N + scoreCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + scoreCheckBoxActionPerformed(evt); + } + }); + + scoreList.setModel(new DefaultListModel()); + scoreList.setEnabled(false); + jScrollPane11.setViewportView(scoreList); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -846,22 +881,18 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addGap(344, 344, 344)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(hashCheckBox) - .addGap(18, 18, 18) - .addComponent(jScrollPane7)) - .addGroup(layout.createSequentialGroup() - .addComponent(intCheckBox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane8)) - .addGroup(layout.createSequentialGroup() - .addComponent(tagsCheckBox) - .addGap(18, 18, 18) - .addComponent(jScrollPane9)) - .addGroup(layout.createSequentialGroup() - .addComponent(objCheckBox) - .addGap(18, 18, 18) - .addComponent(jScrollPane10))) + .addComponent(intCheckBox) + .addComponent(tagsCheckBox) + .addComponent(objCheckBox) + .addComponent(hashCheckBox) + .addComponent(scoreCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jScrollPane11) + .addComponent(jScrollPane7, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane10, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane9, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane8, javax.swing.GroupLayout.Alignment.LEADING)) .addGap(35, 35, 35) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel3) @@ -878,8 +909,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(exifCheckBox) - .addComponent(notableCheckBox) - .addComponent(jCheckBox3)) + .addComponent(notableCheckBox)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); layout.setVerticalGroup( @@ -929,27 +959,28 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe .addComponent(jScrollPane8, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(intCheckBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane5, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(kwCheckBox) - .addGroup(layout.createSequentialGroup() - .addComponent(exifCheckBox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(notableCheckBox))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(scoreCheckBox) + .addComponent(jScrollPane11, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(parentCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel2)) .addComponent(jScrollPane6, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jCheckBox3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createSequentialGroup() + .addComponent(exifCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(notableCheckBox))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(deleteParentButton) .addComponent(parentFullRadioButton) .addComponent(parentSubstringRadioButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(addParentButton) .addComponent(parentTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1045,6 +1076,10 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe // TODO add your handling code here: }//GEN-LAST:event_notableCheckBoxActionPerformed + private void scoreCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scoreCheckBoxActionPerformed + scoreList.setEnabled(scoreCheckBox.isSelected()); + }//GEN-LAST:event_scoreCheckBoxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton addParentButton; private javax.swing.JButton cancelButton; @@ -1064,7 +1099,6 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JList hashList; private javax.swing.JCheckBox intCheckBox; private javax.swing.JList intList; - private javax.swing.JCheckBox jCheckBox3; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; @@ -1072,6 +1106,7 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JLabel jLabel5; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane10; + private javax.swing.JScrollPane jScrollPane11; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; private javax.swing.JScrollPane jScrollPane4; @@ -1094,6 +1129,8 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe private javax.swing.JList parentList; private javax.swing.JRadioButton parentSubstringRadioButton; private javax.swing.JTextField parentTextField; + private javax.swing.JCheckBox scoreCheckBox; + private javax.swing.JList scoreList; private javax.swing.JButton searchButton; private javax.swing.JCheckBox sizeCheckBox; private javax.swing.JList sizeList; diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index 10b7574d08..5d3b74ebfe 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -26,6 +26,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; +import org.sleuthkit.autopsy.filequery.FileSearchData.Score; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DataSource; @@ -36,9 +37,11 @@ import org.sleuthkit.datamodel.TskCoreException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import java.util.stream.Collectors; import org.openide.util.NbBundle; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskData; /** * Run various filters to return a subset of files from the current case. @@ -642,6 +645,83 @@ class FileSearchFiltering { } } + /** + * A filter for specifying the score. + * A file must have one of the given scores to pass + */ + static class ScoreFilter extends FileFilter { + private final List scores; + + /** + * Create the ObjectDetectionFilter + * @param typeNames + */ + ScoreFilter(List scores) { + this.scores = scores; + } + + @Override + String getWhereClause() { + + // Current algorithm: + // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag. + // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag. + String hashsetQueryPart = ""; + String tagQueryPart = ""; + String intItemQueryPart = ""; + + if (scores.contains(Score.NOTABLE)) { + // do hashset + hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") "; + } + + if (scores.contains(Score.INTERESTING)) { + // Matches interesting item artifact + intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = " + + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) "; + } + + if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) { + // Any tag will work + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))"; + } else if (scores.contains(Score.NOTABLE)) { + // Notable tags + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = " + + TskData.FileKnown.BAD.getFileKnownValue() + ")))"; + } else if (scores.contains(Score.INTERESTING)) { + // Non-notable tags + tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != " + + TskData.FileKnown.BAD.getFileKnownValue() + ")))"; + } + + String queryStr = hashsetQueryPart; + if (! intItemQueryPart.isEmpty()) { + if (! queryStr.isEmpty()) { + queryStr += " OR "; + } + queryStr += intItemQueryPart; + } + if (! tagQueryPart.isEmpty()) { + if (! queryStr.isEmpty()) { + queryStr += " OR "; + } + queryStr += tagQueryPart; + } + System.out.println("\n#### query for score\n" + queryStr + "\n"); + return queryStr; + } + + @NbBundle.Messages({ + "# {0} - filters", + "FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}", + }) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_ScoreFilter_desc( + concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList()))); + } + } + /** * A filter for specifying tag names. * A file must contain one of the given tags to pass. From 306cd30b61923761e731d9720948dce7b3e00174 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Mon, 15 Jul 2019 14:53:32 -0400 Subject: [PATCH 07/11] Removed debug print --- .../src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index 5d3b74ebfe..cd98d2d610 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -707,7 +707,6 @@ class FileSearchFiltering { } queryStr += tagQueryPart; } - System.out.println("\n#### query for score\n" + queryStr + "\n"); return queryStr; } From 2cc241797b10a8844efeb612d3a368213a9cfc30 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 16 Jul 2019 11:32:14 -0400 Subject: [PATCH 08/11] codacy changes --- .../autopsy/filequery/FileSearchDialog.java | 12 ++++++++++-- .../autopsy/filequery/FileSearchFiltering.java | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index a22e12aaa8..0f5fc222db 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -53,6 +53,7 @@ import org.sleuthkit.datamodel.TagName; /** * Dialog to allow the user to choose filtering and grouping options. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class FileSearchDialog extends javax.swing.JDialog implements ActionListener { private final static Logger logger = Logger.getLogger(FileSearchDialog.class.getName()); @@ -281,6 +282,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe addListeners(tagsCheckBox, tagsList); } + /** + * TagsListCellRenderer + */ private class TagsListCellRenderer extends DefaultListCellRenderer { @Override @@ -290,14 +294,18 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe int index, boolean isSelected, boolean cellHasFocus) { + Object newValue = value; if (value instanceof TagName) { - value = ((TagName)value).getDisplayName(); + newValue = ((TagName)value).getDisplayName(); } - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); return this; } } + /** + * Initialize the object filter + */ private void setUpObjectFilter() { int count = 0; try { diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index cd98d2d610..5af83dfe20 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -778,6 +778,7 @@ class FileSearchFiltering { * Create the ExifFilter */ ExifFilter() { + // Nothing to save } @Override @@ -807,6 +808,7 @@ class FileSearchFiltering { * Create the NotableFilter */ NotableFilter() { + // Nothing to save } @Override From 0184606228485bd7652eb1e76cc1912c02d41e32 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 16 Jul 2019 15:05:32 -0400 Subject: [PATCH 09/11] Addressing review comments --- .../autopsy/filequery/FileSearch.java | 44 +++++++++---------- .../filequery/FileSearchTestAction.java | 4 +- .../autopsy/filequery/ResultFile.java | 4 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index a8f8904f0c..23e19b4abb 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -131,7 +131,7 @@ class FileSearch { } /** - * Run the file search to get the group names and sizes. + * Run the file search to get the files in a given group. * * @param filters The filters to apply * @param groupAttributeType The attribute to use for grouping @@ -147,7 +147,7 @@ class FileSearch { * * @throws FileSearchException */ - static List getGroupEntries( + static List getFilesInGroup( List filters, AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, @@ -633,7 +633,7 @@ class FileSearch { // Get pairs of (object ID, keyword list name) for all files in the list of files that have // keyword list hits. - String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(files); @@ -850,12 +850,12 @@ class FileSearch { void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // Get pairs of (object ID, hash set name) for all files in the list of files that have // hash set hits. - String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); - SetHashSetNamesCallback callback = new SetHashSetNamesCallback(files); + HashSetNamesCallback callback = new HashSetNamesCallback(files); try { caseDb.getCaseDbAccessManager().select(selectQuery, callback); } catch (TskCoreException ex) { @@ -865,9 +865,9 @@ class FileSearch { /** * Callback to process the results of the CaseDbAccessManager select query. Will add - * the keyword list names to the list of ResultFile objects. + * the hash set names to the list of ResultFile objects. */ - private static class SetHashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + private static class HashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { List resultFiles; @@ -876,7 +876,7 @@ class FileSearch { * * @param resultFiles List of files to add hash set names to */ - SetHashSetNamesCallback(List resultFiles) { + HashSetNamesCallback(List resultFiles) { this.resultFiles = resultFiles; } @@ -988,12 +988,12 @@ class FileSearch { void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // Get pairs of (object ID, interesting item set name) for all files in the list of files that have // interesting file set hits. - String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()); - SetInterstingFileSetNamesCallback callback = new SetInterstingFileSetNamesCallback(files); + InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(files); try { caseDb.getCaseDbAccessManager().select(selectQuery, callback); } catch (TskCoreException ex) { @@ -1005,7 +1005,7 @@ class FileSearch { * Callback to process the results of the CaseDbAccessManager select query. Will add * the interesting file set names to the list of ResultFile objects. */ - private static class SetInterstingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + private static class InterestingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { List resultFiles; @@ -1014,7 +1014,7 @@ class FileSearch { * * @param resultFiles List of files to add interesting file set names to */ - SetInterstingFileSetNamesCallback(List resultFiles) { + InterestingFileSetNamesCallback(List resultFiles) { this.resultFiles = resultFiles; } @@ -1126,12 +1126,12 @@ class FileSearch { void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - // Get pairs of (object ID, keyword list name) for all files in the list of files that have + // Get pairs of (object ID, object type name) for all files in the list of files that have // objects detected - String selectQuery = createSetNameQuery(files, caseDb, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(), + String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID()); - SetObjectDetectedNamesCallback callback = new SetObjectDetectedNamesCallback(files); + ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(files); try { caseDb.getCaseDbAccessManager().select(selectQuery, callback); } catch (TskCoreException ex) { @@ -1141,9 +1141,9 @@ class FileSearch { /** * Callback to process the results of the CaseDbAccessManager select query. Will add - * the interesting file set names to the list of ResultFile objects. + * the object type names to the list of ResultFile objects. */ - private static class SetObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + private static class ObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { List resultFiles; @@ -1152,7 +1152,7 @@ class FileSearch { * * @param resultFiles List of files to add object detected names to */ - SetObjectDetectedNamesCallback(List resultFiles) { + ObjectDetectedNamesCallback(List resultFiles) { this.resultFiles = resultFiles; } @@ -1280,7 +1280,7 @@ class FileSearch { } /** - * Key representing a interesting item set group + * Key representing a file tag group */ private static class FileTagGroupKey extends GroupKey { private final List tagNames; @@ -1404,7 +1404,7 @@ class FileSearch { } } - private static String createSetNameQuery(List files, SleuthkitCase caseDb, + private static String createSetNameClause(List files, int artifactTypeID, int setNameAttrID) throws FileSearchException { // Concatenate the object IDs in the list of files diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java index 90fbcafa47..d69c85b8a1 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java @@ -106,7 +106,7 @@ public final class FileSearchTestAction extends CallableSystemAction { if (groups.size() > 0) { String firstGroupName = groups.keySet().iterator().next(); - List entries0to5 = FileSearch.getGroupEntries(filters, + List entries0to5 = FileSearch.getFilesInGroup(filters, groupingAttr, groupSortAlgorithm, fileSort, @@ -119,7 +119,7 @@ public final class FileSearchTestAction extends CallableSystemAction { System.out.println(" " + f.getName()); } - List entries6to106 = FileSearch.getGroupEntries(filters, + List entries6to106 = FileSearch.getFilesInGroup(filters, groupingAttr, groupSortAlgorithm, fileSort, diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java index 8d6c176d01..bb0d45f9a3 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -118,7 +118,7 @@ class ResultFile { /** * Add a hash set name that matched this file. * - * @param keywordListName + * @param hashSetName */ void addHashSetName (String hashSetName) { if (! hashSetNames.contains(hashSetName)) { @@ -139,7 +139,7 @@ class ResultFile { } /** - * Add a hash set name that matched this file. + * Add a tag name that matched this file. * * @param tagName */ From 6a8f125b6fe9d3d00cb9bd9523c869f6bdbefad0 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 17 Jul 2019 08:14:08 -0400 Subject: [PATCH 10/11] codacy --- .../org/sleuthkit/autopsy/filequery/FileSearchTestAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java index d69c85b8a1..17e6acbf8a 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java @@ -103,7 +103,7 @@ public final class FileSearchTestAction extends CallableSystemAction { System.out.println(" " + name + " : " + groups.get(name)); } - if (groups.size() > 0) { + if (! groups.isEmpty()) { String firstGroupName = groups.keySet().iterator().next(); List entries0to5 = FileSearch.getFilesInGroup(filters, From bb5681a1da55e61a336a7de49693b72fed0c17f7 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 17 Jul 2019 13:42:28 -0400 Subject: [PATCH 11/11] Add additional options for CR frequency --- .../filequery/Bundle.properties-MERGED | 8 +++- .../autopsy/filequery/FileSearchData.java | 40 +++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 260d33c651..dd8ff7dd19 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -33,8 +33,12 @@ FileSearchData.FileType.Image.displayName=Image FileSearchData.FileType.Other.displayName=Other/Unknown FileSearchData.FileType.Video.displayName=Video FileSearchData.Frequency.common.displayName=Common -FileSearchData.Frequency.rare.displayName=Rare -FileSearchData.Frequency.unique.displayName=Unique +FileSearchData.Frequency.count_10.displayName=6 - 10 +FileSearchData.Frequency.count_100.displayName=51 - 100 +FileSearchData.Frequency.count_20.displayName=11 - 20 +FileSearchData.Frequency.count_50.displayName=21 - 50 +FileSearchData.Frequency.rare.displayName=Rare (2-5) +FileSearchData.Frequency.unique.displayName=Unique (1) FileSearchData.Frequency.unknown.displayName=Unknown FileSearchData.Score.interesting.displayName=Interesting FileSearchData.Score.notable.displayName=Notable diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java index 00ff9ff8de..db488db8f7 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java @@ -34,24 +34,32 @@ class FileSearchData { * Enum representing how often the file occurs in the Central Repository. */ @NbBundle.Messages({ - "FileSearchData.Frequency.unique.displayName=Unique", - "FileSearchData.Frequency.rare.displayName=Rare", + "FileSearchData.Frequency.unique.displayName=Unique (1)", + "FileSearchData.Frequency.rare.displayName=Rare (2-5)", + "FileSearchData.Frequency.count_10.displayName=6 - 10", + "FileSearchData.Frequency.count_20.displayName=11 - 20", + "FileSearchData.Frequency.count_50.displayName=21 - 50", + "FileSearchData.Frequency.count_100.displayName=51 - 100", "FileSearchData.Frequency.common.displayName=Common", "FileSearchData.Frequency.unknown.displayName=Unknown", }) enum Frequency { - UNIQUE(0, Bundle.FileSearchData_Frequency_unique_displayName()), - RARE(1, Bundle.FileSearchData_Frequency_rare_displayName()), - COMMON(2, Bundle.FileSearchData_Frequency_common_displayName()), - UNKNOWN(3, Bundle.FileSearchData_Frequency_unknown_displayName()); + UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()), + RARE(1, 5, Bundle.FileSearchData_Frequency_rare_displayName()), + COUNT_10(1, 10, Bundle.FileSearchData_Frequency_count_10_displayName()), + COUNT_20(1, 10, Bundle.FileSearchData_Frequency_count_20_displayName()), + COUNT_50(1, 10, Bundle.FileSearchData_Frequency_count_50_displayName()), + COUNT_100(1, 10, Bundle.FileSearchData_Frequency_count_100_displayName()), + COMMON(2, 0, Bundle.FileSearchData_Frequency_common_displayName()), + UNKNOWN(3, 0, Bundle.FileSearchData_Frequency_unknown_displayName()); private final int ranking; private final String displayName; - static private final long uniqueMax = 1; - static private final long rareMax = 5; + private final int maxOccur; - Frequency(int ranking, String displayName) { + Frequency(int ranking, int maxOccur, String displayName) { this.ranking = ranking; + this.maxOccur = maxOccur; this.displayName = displayName; } @@ -72,10 +80,18 @@ class FileSearchData { * @return the corresponding enum */ static Frequency fromCount(long count) { - if (count <= uniqueMax) { + if (count <= UNIQUE.maxOccur) { return UNIQUE; - } else if (count < rareMax) { + } else if (count <= RARE.maxOccur) { return RARE; + } else if (count <= COUNT_10.maxOccur) { + return COUNT_10; + } else if (count <= COUNT_20.maxOccur) { + return COUNT_20; + } else if (count <= COUNT_50.maxOccur) { + return COUNT_50; + } else if (count <= COUNT_100.maxOccur) { + return COUNT_100; } return COMMON; } @@ -86,7 +102,7 @@ class FileSearchData { * @return enums that can be used to filter */ static List getOptionsForFiltering() { - return Arrays.asList(UNIQUE, RARE, COMMON); + return Arrays.asList(UNIQUE, RARE, COUNT_10, COUNT_20, COUNT_50, COUNT_100, COMMON); } @Override