From f57aabed574fb1c612fb2678991eea5325821507 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 24 Oct 2019 17:13:14 -0400 Subject: [PATCH 1/3] 5674 modify parent path filter to allow option of excluding directories --- .../autopsy/filequery/Bundle.properties | 2 + .../filequery/Bundle.properties-MERGED | 10 +- .../autopsy/filequery/FileSearchDialog.java | 4 +- .../filequery/FileSearchFiltering.java | 597 ++++++++++-------- .../autopsy/filequery/FileSearchPanel.form | 47 +- .../autopsy/filequery/FileSearchPanel.java | 128 ++-- 6 files changed, 461 insertions(+), 327 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 4bd039d880..6577de6054 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -58,3 +58,5 @@ ResultsPanel.gotoPageLabel.text=Go to Page: ResultsPanel.pageSizeLabel.text=Page size: ResultsPanel.instancesList.border.title=Instances DiscoveryExtractAction.title.extractFiles.text=Extract File +FileSearchPanel.includeRadioButton.text=Include +FileSearchPanel.excludeRadioButton.text=Exclude diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 0c2e867c95..94be3fb81b 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -93,10 +93,10 @@ FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} FileSearchFiltering.ParentFilter.exact=(exact match) FileSearchFiltering.ParentFilter.or=\ or FileSearchFiltering.ParentFilter.substring=(substring) -# {0} - search term -FileSearchFiltering.ParentSearchTerm.fullString=\ {0} (exact) -# {0} - search term -FileSearchFiltering.ParentSearchTerm.subString=\ {0} (substring) +FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude) +FileSearchFiltering.ParentSearchTerm.fullString=\ (exact) +FileSearchFiltering.ParentSearchTerm.includeString=\ (include) +FileSearchFiltering.ParentSearchTerm.subString=\ (substring) FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable # {0} - filters FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0} @@ -177,6 +177,8 @@ ResultsPanel.gotoPageLabel.text=Go to Page: ResultsPanel.pageSizeLabel.text=Page size: ResultsPanel.instancesList.border.title=Instances DiscoveryExtractAction.title.extractFiles.text=Extract File +FileSearchPanel.includeRadioButton.text=Include +FileSearchPanel.excludeRadioButton.text=Exclude ResultsPanel.viewFileInDir.name=View File in Directory SearchNode.getName.text=Search Result # {0} - numberOfInstances diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java index 628bfec89f..37e7912c4f 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchDialog.java @@ -1092,9 +1092,9 @@ public class FileSearchDialog extends javax.swing.JDialog implements ActionListe if ( ! parentTextField.getText().isEmpty()) { ParentSearchTerm searchTerm; if (parentFullRadioButton.isSelected()) { - searchTerm = new ParentSearchTerm(parentTextField.getText(), true); + searchTerm = new ParentSearchTerm(parentTextField.getText(), true, true); } else { - searchTerm = new ParentSearchTerm(parentTextField.getText(), false); + searchTerm = new ParentSearchTerm(parentTextField.getText(), false, true); } parentListModel.add(parentListModel.size(), searchTerm); } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index f0da5c1807..8cf2c455e2 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -47,52 +47,52 @@ import org.sleuthkit.datamodel.TskData; * Run various filters to return a subset of files from the current case. */ class FileSearchFiltering { - + private final static Logger logger = Logger.getLogger(FileSearchFiltering.class.getName()); - + /** * Run the given filters to get a list of matching files. - * - * @param filters The filters to run - * @param caseDb The case database - * @param crDb The central repo. Can be null as long as no filters need it. - * - * @return + * + * @param filters The filters to run + * @param caseDb The case database + * @param crDb The central repo. Can be null as long as no filters need + * it. + * + * @return */ static List runQueries(List filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - + if (caseDb == null) { throw new FileSearchException("Case DB parameter is null"); // NON-NLS } - + // Record the selected filters String filterStr = ""; for (FileFilter filter : filters) { filterStr += " " + filter.getDesc() + "\n"; } logger.log(Level.INFO, "Running filters:\n{0}", filterStr); - + // Combine all the SQL queries from the filters into one query String combinedQuery = ""; - for (FileFilter filter : filters) { - if ( ! filter.getWhereClause().isEmpty()) { - if ( ! combinedQuery.isEmpty()) { + for (FileFilter filter : filters) { + if (!filter.getWhereClause().isEmpty()) { + if (!combinedQuery.isEmpty()) { combinedQuery += " AND "; // NON-NLS } combinedQuery += "(" + filter.getWhereClause() + ")"; // NON-NLS } } - + if (combinedQuery.isEmpty()) { // The file search filter is required, so this should never be empty. throw new FileSearchException("Selected filters do not include a case database query"); } - + try { // Get all matching abstract files List resultList = new ArrayList<>(); - logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery); List sqlResults = caseDb.findAllFilesWhere(combinedQuery); @@ -105,89 +105,99 @@ class FileSearchFiltering { for (AbstractFile abstractFile : sqlResults) { resultList.add(new ResultFile(abstractFile)); } - + // Now run any non-SQL filters. for (FileFilter filter : filters) { if (filter.useAlternateFilter()) { resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb); } - + // There are no matches for the filters run so far, so return if (resultList.isEmpty()) { return resultList; } } - + return resultList; } catch (TskCoreException ex) { throw new FileSearchException("Error querying case database", ex); // NON-NLS } } - + /** * Base class for the filters. */ - static abstract class FileFilter { + static abstract class FileFilter { + /** - * Returns part of a query on the tsk_files table that can be AND-ed with other pieces - * @return the SQL query or an empty string if there is no SQL query for this filter. + * Returns part of a query on the tsk_files table that can be AND-ed + * with other pieces + * + * @return the SQL query or an empty string if there is no SQL query for + * this filter. */ abstract String getWhereClause(); - + /** - * Indicates whether this filter needs to use the secondary, non-SQL method applyAlternateFilter(). + * Indicates whether this filter needs to use the secondary, non-SQL + * method applyAlternateFilter(). + * * @return false by default */ boolean useAlternateFilter() { return false; } - + /** * Run a secondary filter that does not operate on tsk_files. - * - * @param currentResults The current list of matching files; empty if no filters have yet been run. + * + * @param currentResults The current list of matching files; empty if no + * filters have yet been run. * @param caseDb The case database - * @param centralRepoDb The central repo database. Can be null if the filter does not require it. - * - * @return The list of files that match this filter (and any that came before it) - * - * @throws FileSearchException + * @param centralRepoDb The central repo database. Can be null if the + * filter does not require it. + * + * @return The list of files that match this filter (and any that came + * before it) + * + * @throws FileSearchException */ - List applyAlternateFilter (List currentResults, SleuthkitCase caseDb, + List applyAlternateFilter(List currentResults, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { return new ArrayList<>(); } - + /** * Get a description of the selected filter. - * + * * @return A description of the filter */ abstract String getDesc(); } - + /** * A filter for specifying the file size */ static class SizeFilter extends FileFilter { + private final List fileSizes; - + /** * Create the SizeFilter - * + * * @param fileSizes the file sizes that should match */ SizeFilter(List fileSizes) { this.fileSizes = fileSizes; } - + @Override String getWhereClause() { String queryStr = ""; // NON-NLS for (FileSize size : fileSizes) { - if (! queryStr.isEmpty()) { + if (!queryStr.isEmpty()) { queryStr += " OR "; // NON-NLS - } + } if (size.getMaxBytes() != FileSize.NO_MAXIMUM) { queryStr += "(size > \'" + size.getMinBytes() + "\' AND size <= \'" + size.getMaxBytes() + "\')"; // NON-NLS } else { @@ -196,20 +206,19 @@ class FileSearchFiltering { } return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}", "FileSearchFiltering.SizeFilter.or= or ", "# {0} - Minimum bytes", "# {1} - Maximum bytes", - "FileSearchFiltering.SizeFilter.range=({0} to {1})", - }) + "FileSearchFiltering.SizeFilter.range=({0} to {1})",}) @Override String getDesc() { String desc = ""; // NON-NLS for (FileSize size : fileSizes) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_SizeFilter_or(); } desc += Bundle.FileSearchFiltering_SizeFilter_range(size.getMinBytes(), size.getMaxBytes()); @@ -218,98 +227,150 @@ class FileSearchFiltering { return desc; } } - + /** - * A utility class for the ParentFilter to store the search string - * and whether it is a full path or a substring. + * A utility class for the ParentFilter to store the search string and + * whether it is a full path or a substring. */ static class ParentSearchTerm { + private final String searchStr; private final boolean isFullPath; - + private final boolean isIncluded; + /** * Create the ParentSearchTerm object - * + * * @param searchStr The string to search for in the file path - * @param isFullPath True if the path should exactly match the given + * @param isFullPath True if the path should exactly match the given * string, false to do a substring search + * @param isIncluded True if the results must include the path, false if + * the path should be excluded from the results. */ - ParentSearchTerm(String searchStr, boolean isFullPath) { + ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded) { this.searchStr = searchStr; this.isFullPath = isFullPath; + this.isIncluded = isIncluded; } - + /** * Get the SQL term to search for - * + * * @return The SQL for a where clause to search for a matching path */ String getSQLForTerm() { // TODO - these should really be prepared statements - if (isFullPath) { - return "parent_path=\'" + searchStr + "\'"; // NON-NLS + if (isIncluded()) { + if (isFullPath()) { + return "parent_path=\'" + searchStr + "\'"; // NON-NLS + } else { + return "parent_path LIKE \'%" + searchStr + "%\'"; // NON-NLS + } } else { - return "parent_path LIKE \'%" + searchStr + "%\'"; // NON-NLS + if (isFullPath()) { + return "parent_path!=\'" + searchStr + "\'"; // NON-NLS + } else { + return "parent_path NOT LIKE \'%" + searchStr + "%\'"; // NON-NLS + } } } - + @NbBundle.Messages({ - "# {0} - search term", - "FileSearchFiltering.ParentSearchTerm.fullString= {0} (exact)", - "# {0} - search term", - "FileSearchFiltering.ParentSearchTerm.subString= {0} (substring)", - }) + "FileSearchFiltering.ParentSearchTerm.fullString= (exact)", + "FileSearchFiltering.ParentSearchTerm.subString= (substring)", + "FileSearchFiltering.ParentSearchTerm.includeString= (include)", + "FileSearchFiltering.ParentSearchTerm.excludeString= (exclude)",}) @Override public String toString() { - if (isFullPath) { - return Bundle.FileSearchFiltering_ParentSearchTerm_fullString(searchStr); + String returnString = searchStr; + if (isFullPath()) { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_fullString(); + } else { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_subString(); } - return Bundle.FileSearchFiltering_ParentSearchTerm_subString(searchStr); + if (isIncluded()) { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_includeString(); + } else { + returnString += Bundle.FileSearchFiltering_ParentSearchTerm_excludeString(); + } + return returnString; + } + + /** + * @return the isFullPath + */ + boolean isFullPath() { + return isFullPath; + } + + /** + * @return the isIncluded + */ + boolean isIncluded() { + return isIncluded; } } - + /** * A filter for specifying parent path (either full path or substring) - */ + */ static class ParentFilter extends FileFilter { + private final List parentSearchTerms; - + /** * Create the ParentFilter - * + * * @param parentSearchTerms Full paths or substrings to filter on */ ParentFilter(List parentSearchTerms) { this.parentSearchTerms = parentSearchTerms; } - + @Override String getWhereClause() { - String queryStr = ""; // NON-NLS + String includeQueryStr = ""; // NON-NLS + String excludeQueryStr = ""; for (ParentSearchTerm searchTerm : parentSearchTerms) { - if (! queryStr.isEmpty()) { - queryStr += " OR "; // NON-NLS - } - queryStr += searchTerm.getSQLForTerm(); + if (searchTerm.isIncluded()) { + if (!includeQueryStr.isEmpty()) { + includeQueryStr += " OR "; // NON-NLS + } + includeQueryStr += searchTerm.getSQLForTerm(); + } else { + if (!excludeQueryStr.isEmpty()) { + excludeQueryStr += " AND "; // NON-NLS + } + excludeQueryStr += searchTerm.getSQLForTerm(); + } + } + if (!includeQueryStr.isEmpty()) { + includeQueryStr = "(" + includeQueryStr + ")"; + } + if (!excludeQueryStr.isEmpty()) { + excludeQueryStr = "(" + excludeQueryStr + ")"; + } + if (includeQueryStr.isEmpty() || excludeQueryStr.isEmpty()) { + return includeQueryStr + excludeQueryStr; + } else { + return includeQueryStr + " AND " + excludeQueryStr; } - return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}", "FileSearchFiltering.ParentFilter.or= or ", "FileSearchFiltering.ParentFilter.exact=(exact match)", - "FileSearchFiltering.ParentFilter.substring=(substring)", - }) + "FileSearchFiltering.ParentFilter.substring=(substring)",}) @Override String getDesc() { String desc = ""; // NON-NLS for (ParentSearchTerm searchTerm : parentSearchTerms) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_ParentFilter_or(); } - if (searchTerm.isFullPath) { + if (searchTerm.isFullPath()) { desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentFilter_exact(); } else { desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentFilter_substring(); @@ -319,48 +380,48 @@ class FileSearchFiltering { return desc; } } - + /** * A filter for specifying data sources - */ + */ static class DataSourceFilter extends FileFilter { + private final List dataSources; - + /** * Create the DataSourceFilter - * + * * @param dataSources the data sources to filter on */ DataSourceFilter(List dataSources) { this.dataSources = dataSources; } - + @Override String getWhereClause() { String queryStr = ""; // NON-NLS for (DataSource ds : dataSources) { - if (! queryStr.isEmpty()) { + if (!queryStr.isEmpty()) { queryStr += ","; // NON-NLS - } + } queryStr += "\'" + ds.getId() + "\'"; // NON-NLS } queryStr = "data_source_obj_id IN (" + queryStr + ")"; // NON-NLS return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}", "FileSearchFiltering.DataSourceFilter.or= or ", "# {0} - Data source name", "# {1} - Data source ID", - "FileSearchFiltering.DataSourceFilter.datasource={0}({1})", - }) + "FileSearchFiltering.DataSourceFilter.datasource={0}({1})",}) @Override String getDesc() { String desc = ""; // NON-NLS for (DataSource ds : dataSources) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_DataSourceFilter_or(); } desc += Bundle.FileSearchFiltering_DataSourceFilter_datasource(ds.getName(), ds.getId()); @@ -369,57 +430,60 @@ class FileSearchFiltering { return desc; } } - + /** - * A filter for specifying keyword list names. - * A file must contain a keyword from one of the given lists to pass. + * A filter for specifying keyword list names. A file must contain a keyword + * from one of the given lists to pass. */ static class KeywordListFilter extends FileFilter { + private final List listNames; - + /** * Create the KeywordListFilter - * @param listNames + * + * @param listNames */ KeywordListFilter(List listNames) { this.listNames = listNames; } - + @Override String getWhereClause() { 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 " + - "AND (" + keywordListPart + "))))"; // NON-NLS - + + 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 " + + "AND (" + keywordListPart + "))))"; // NON-NLS + return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}", - }) + "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}",}) @Override String getDesc() { return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames)); } - } - + } + /** * A filter for specifying file types. - */ + */ static class FileTypeFilter extends FileFilter { + private final List categories; - + /** * Create the FileTypeFilter + * * @param categories List of file types to filter on */ FileTypeFilter(List categories) { this.categories = categories; } - + /** * Create the FileTypeFilter * @@ -435,26 +499,25 @@ class FileSearchFiltering { String queryStr = ""; // NON-NLS for (FileType cat : categories) { for (String type : cat.getMediaTypes()) { - if (! queryStr.isEmpty()) { + if (!queryStr.isEmpty()) { queryStr += ","; // NON-NLS - } + } queryStr += "\'" + type + "\'"; // NON-NLS } } queryStr = "mime_type IN (" + queryStr + ")"; // NON-NLS return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}", - "FileSearchFiltering.FileTypeFilter.or= or ", - }) + "FileSearchFiltering.FileTypeFilter.or= or ",}) @Override String getDesc() { String desc = ""; for (FileType cat : categories) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_FileTypeFilter_or(); } desc += cat.toString(); @@ -462,44 +525,44 @@ class FileSearchFiltering { desc = Bundle.FileSearchFiltering_FileTypeFilter_desc(desc); return desc; } - } - + } + /** * A filter for specifying frequency in the central repository. */ static class FrequencyFilter extends FileFilter { - + private final List frequencies; - + /** * Create the FrequencyFilter - * + * * @param frequencies List of frequencies that will pass the filter */ FrequencyFilter(List frequencies) { this.frequencies = frequencies; } - + @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, + List applyAlternateFilter(List currentResults, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - + if (centralRepoDb == null) { throw new FileSearchException("Can not run Frequency 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()) { @@ -519,17 +582,16 @@ class FileSearchFiltering { } return frequencyResults; } - + @NbBundle.Messages({ "# {0} - filters", "FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}", - "FileSearchFiltering.FrequencyFilter.or= or ", - }) + "FileSearchFiltering.FrequencyFilter.or= or ",}) @Override String getDesc() { String desc = ""; // NON-NLS for (Frequency freq : frequencies) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_FrequencyFilter_or(); } desc += freq.name(); @@ -537,319 +599,324 @@ class FileSearchFiltering { return Bundle.FileSearchFiltering_FrequencyFilter_desc(desc); } } - + /** - * A filter for specifying hash set names. - * A file must match one of the given sets to pass. + * 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 + * + * @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 - + + 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}", - }) + "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. + * 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 + * + * @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 - + + 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}", - }) + "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. + * 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 + * + * @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 - + + 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}", - }) + "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 the score. - * A file must have one of the given scores to pass + * 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 + * + * @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() + ")) "; + 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() + ")))"; + 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() + ")))"; + 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()) { + if (!intItemQueryPart.isEmpty()) { + if (!queryStr.isEmpty()) { queryStr += " OR "; } queryStr += intItemQueryPart; } - if (! tagQueryPart.isEmpty()) { - if (! queryStr.isEmpty()) { + if (!tagQueryPart.isEmpty()) { + if (!queryStr.isEmpty()) { queryStr += " OR "; } queryStr += tagQueryPart; } return queryStr; } - + @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}", - }) + "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. + * 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 + * + * @param tagNames */ TagsFilter(List tagNames) { this.tagNames = tagNames; } - + @Override String getWhereClause() { String tagIDs = ""; // NON-NLS for (TagName tagName : tagNames) { - if (! tagIDs.isEmpty()) { + 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 ", - }) + "FileSearchFiltering.TagsFilter.or= or ",}) @Override String getDesc() { String desc = ""; // NON-NLS for (TagName name : tagNames) { - if ( ! desc.isEmpty()) { + if (!desc.isEmpty()) { desc += Bundle.FileSearchFiltering_TagsFilter_or(); } desc += name.getDisplayName(); } 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() { // Nothing to save } - + @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() + ")))"; - + 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", - }) + "FileSearchFiltering.ExifFilter.desc=Files that contain EXIF data",}) @Override String getDesc() { - return Bundle.FileSearchFiltering_ExifFilter_desc(); + return Bundle.FileSearchFiltering_ExifFilter_desc(); } - } - + } + /** - * A filter for specifying that the file must have been marked as notable in the CR. + * 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() { // Nothing to save } - + @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, + 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.getFirstInstance().getMd5Hash() != null && ! file.getFirstInstance().getMd5Hash().isEmpty()) { - + if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) { + // Check if this file hash is marked as notable in the CR String value = file.getFirstInstance().getMd5Hash(); if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) { @@ -862,50 +929,48 @@ class FileSearchFiltering { throw new FileSearchException("Error querying central repository", ex); // NON-NLS } } - + @NbBundle.Messages({ - "FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable", - }) + "FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable",}) @Override String getDesc() { - return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); + return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); } - } - + } + @NbBundle.Messages({ - "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ", - }) + "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",}) private static String concatenateSetNamesForDisplay(List setNames) { String desc = ""; // NON-NLS for (String setName : setNames) { - if ( ! desc.isEmpty()) { + 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. - * + * 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()) { + 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/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form index 5d319cb4f9..bf956d08c7 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form @@ -2,10 +2,12 @@
- + + + @@ -203,7 +205,7 @@ - + @@ -220,7 +222,7 @@ - + @@ -239,7 +241,7 @@ - + @@ -264,7 +266,7 @@ - + @@ -289,7 +291,7 @@ - + @@ -641,6 +643,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java index e867ec9b0d..4b4865ea32 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java @@ -58,10 +58,10 @@ import org.sleuthkit.datamodel.TagName; * Dialog to allow the user to choose filtering and grouping options. */ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener { - + private static final long serialVersionUID = 1L; private final static Logger logger = Logger.getLogger(FileSearchPanel.class.getName()); - + private DefaultListModel parentListModel; private final SleuthkitCase caseDb; private final EamDb centralRepoDb; @@ -82,7 +82,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener * Set up all the UI components */ private void customizeComponents() { - + searchButton.setEnabled(false); // Set up the filters @@ -204,7 +204,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener int count = 0; try { DefaultListModel kwListModel = (DefaultListModel) keywordList.getModel(); - + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); for (String name : setNames) { @@ -225,7 +225,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener int count = 0; try { DefaultListModel hashListModel = (DefaultListModel) hashSetList.getModel(); - + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); for (String name : setNames) { @@ -247,7 +247,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener int count = 0; try { DefaultListModel intListModel = (DefaultListModel) interestingItemsList.getModel(); - + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME); for (String name : setNames) { @@ -269,7 +269,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener int count = 0; try { DefaultListModel tagsListModel = (DefaultListModel) tagsList.getModel(); - + List tagNames = caseDb.getTagNamesInUse(); for (TagName name : tagNames) { tagsListModel.add(count, name); @@ -288,9 +288,9 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener * TagsListCellRenderer */ private class TagsListCellRenderer extends DefaultListCellRenderer { - + private static final long serialVersionUID = 1L; - + @Override public java.awt.Component getListCellRendererComponent( JList list, @@ -314,7 +314,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener int count = 0; try { DefaultListModel objListModel = (DefaultListModel) objectsList.getModel(); - + List setNames = getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION); for (String name : setNames) { objListModel.add(count, name); @@ -332,7 +332,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener * Initialize the score filter */ private void setUpScoreFilter() { - + int count = 0; DefaultListModel scoreListModel = (DefaultListModel) scoreList.getModel(); for (Score score : Score.getOptionsForFiltering()) { @@ -374,9 +374,10 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener * Initialize the parent path filter */ private void setUpParentPathFilter() { - parentButtonGroup.add(fullRadioButton); - parentButtonGroup.add(substringRadioButton); + parentPathButtonGroup.add(fullRadioButton); + parentPathButtonGroup.add(substringRadioButton); fullRadioButton.setSelected(true); + includeRadioButton.setSelected(true); parentListModel = (DefaultListModel) parentList.getModel(); addListeners(parentCheckbox, parentList); } @@ -391,57 +392,57 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener // There will always be a file type selected filters.add(new FileSearchFiltering.FileTypeFilter(fileTypeComboBox.getItemAt(fileTypeComboBox.getSelectedIndex()))); - + if (parentCheckbox.isSelected()) { // For the parent paths, everything in the box is used (not just the selected entries) filters.add(new FileSearchFiltering.ParentFilter(getParentPaths())); } - + if (dataSourceCheckbox.isSelected()) { List dataSources = dataSourceList.getSelectedValuesList().stream().map(t -> t.getDataSource()).collect(Collectors.toList()); filters.add(new FileSearchFiltering.DataSourceFilter(dataSources)); } - + if (crFrequencyCheckbox.isSelected()) { filters.add(new FileSearchFiltering.FrequencyFilter(crFrequencyList.getSelectedValuesList())); } - + if (sizeCheckbox.isSelected()) { filters.add(new FileSearchFiltering.SizeFilter(sizeList.getSelectedValuesList())); } - + if (keywordCheckbox.isSelected()) { filters.add(new FileSearchFiltering.KeywordListFilter(keywordList.getSelectedValuesList())); } - + if (hashSetCheckbox.isSelected()) { filters.add(new FileSearchFiltering.HashSetFilter(hashSetList.getSelectedValuesList())); } - + if (interestingItemsCheckbox.isSelected()) { filters.add(new FileSearchFiltering.InterestingFileSetFilter(interestingItemsList.getSelectedValuesList())); } - + if (objectsCheckbox.isSelected()) { filters.add(new FileSearchFiltering.ObjectDetectionFilter(objectsList.getSelectedValuesList())); } - + if (tagsCheckbox.isSelected()) { filters.add(new FileSearchFiltering.TagsFilter(tagsList.getSelectedValuesList())); } - + if (exifCheckbox.isSelected()) { filters.add(new FileSearchFiltering.ExifFilter()); } - + if (notableCheckbox.isSelected()) { filters.add(new FileSearchFiltering.NotableFilter()); } - + if (scoreCheckbox.isSelected()) { filters.add(new FileSearchFiltering.ScoreFilter(scoreList.getSelectedValuesList())); } - + return filters; } @@ -488,7 +489,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener FileSorter.SortingMethod getFileSortingMethod() { return (FileSorter.SortingMethod) orderByCombobox.getSelectedItem(); } - + @Override public void actionPerformed(ActionEvent e) { validateFields(); @@ -499,17 +500,17 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener * name */ private class DataSourceItem { - + private final DataSource ds; - + DataSourceItem(DataSource ds) { this.ds = ds; } - + DataSource getDataSource() { return ds; } - + @Override public String toString() { return ds.getName() + " (ID: " + ds.getId() + ")"; @@ -549,27 +550,27 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener setInvalid("At least one parent path must be entered"); return; } - + if (hashSetCheckbox.isSelected() && hashSetList.getSelectedValuesList().isEmpty()) { setInvalid("At least one hash set name must be selected"); return; } - + if (interestingItemsCheckbox.isSelected() && interestingItemsList.getSelectedValuesList().isEmpty()) { setInvalid("At least one interesting file set name must be selected"); return; } - + if (objectsCheckbox.isSelected() && objectsList.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; } - + if (scoreCheckbox.isSelected() && scoreList.getSelectedValuesList().isEmpty()) { setInvalid("At least one score must be selected"); return; @@ -606,8 +607,9 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; - parentButtonGroup = new javax.swing.ButtonGroup(); + parentPathButtonGroup = new javax.swing.ButtonGroup(); orderGroupsByButtonGroup = new javax.swing.ButtonGroup(); + parentIncludeButtonGroup = new javax.swing.ButtonGroup(); filtersScrollPane = new javax.swing.JScrollPane(); filtersPanel = new javax.swing.JPanel(); sizeCheckbox = new javax.swing.JCheckBox(); @@ -648,6 +650,8 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener interestingItemsList = new javax.swing.JList<>(); scoreScrollPane = new javax.swing.JScrollPane(); scoreList = new javax.swing.JList<>(); + includeRadioButton = new javax.swing.JRadioButton(); + excludeRadioButton = new javax.swing.JRadioButton(); fileTypeLabel = new javax.swing.JLabel(); searchButton = new javax.swing.JButton(); sortingPanel = new javax.swing.JPanel(); @@ -751,7 +755,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); filtersPanel.add(dataSourceScrollPane, gridBagConstraints); - parentButtonGroup.add(fullRadioButton); + parentPathButtonGroup.add(fullRadioButton); fullRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.fullRadioButton.text")); // NOI18N fullRadioButton.setEnabled(false); @@ -762,7 +766,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); filtersPanel.add(fullRadioButton, gridBagConstraints); - parentButtonGroup.add(substringRadioButton); + parentPathButtonGroup.add(substringRadioButton); org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.substringRadioButton.text")); // NOI18N substringRadioButton.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -776,7 +780,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener parentTextField.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 14; + gridBagConstraints.gridy = 15; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; @@ -796,7 +800,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 14; + gridBagConstraints.gridy = 15; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 6); filtersPanel.add(addButton, gridBagConstraints); @@ -813,7 +817,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 13; + gridBagConstraints.gridy = 14; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; gridBagConstraints.insets = new java.awt.Insets(0, 10, 4, 6); filtersPanel.add(deleteButton, gridBagConstraints); @@ -1057,6 +1061,28 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 6); filtersPanel.add(scoreScrollPane, gridBagConstraints); + parentIncludeButtonGroup.add(includeRadioButton); + includeRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.includeRadioButton.text")); // NOI18N + includeRadioButton.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 14; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); + filtersPanel.add(includeRadioButton, gridBagConstraints); + + parentIncludeButtonGroup.add(excludeRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.excludeRadioButton.text")); // NOI18N + excludeRadioButton.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 14; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 0.5; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); + filtersPanel.add(excludeRadioButton, gridBagConstraints); + filtersScrollPane.setViewportView(filtersPanel); org.openide.awt.Mnemonics.setLocalizedText(fileTypeLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.fileTypeLabel.text")); // NOI18N @@ -1195,7 +1221,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed enableSearch(false); - + FileType searchType = fileTypeComboBox.getItemAt(fileTypeComboBox.getSelectedIndex()); DiscoveryEvents.getDiscoveryEventBus().post(new DiscoveryEvents.SearchStartedEvent(searchType)); // For testing, allow the user to run different searches in loop @@ -1270,6 +1296,8 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener deleteButton.setEnabled(enabled && parentCheckbox.isSelected() && !parentListModel.isEmpty()); fullRadioButton.setEnabled(enabled && parentCheckbox.isSelected()); substringRadioButton.setEnabled(enabled && parentCheckbox.isSelected()); + includeRadioButton.setEnabled(enabled && parentCheckbox.isSelected()); + excludeRadioButton.setEnabled(enabled && parentCheckbox.isSelected()); } /** @@ -1300,6 +1328,8 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener parentList.setEnabled(parentCheckbox.isSelected()); fullRadioButton.setEnabled(parentCheckbox.isSelected()); substringRadioButton.setEnabled(parentCheckbox.isSelected()); + includeRadioButton.setEnabled(parentCheckbox.isSelected()); + excludeRadioButton.setEnabled(parentCheckbox.isSelected()); parentTextField.setEnabled(parentCheckbox.isSelected()); addButton.setEnabled(parentCheckbox.isSelected()); deleteButton.setEnabled(parentCheckbox.isSelected() && !parentListModel.isEmpty()); @@ -1320,13 +1350,10 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed if (!parentTextField.getText().isEmpty()) { ParentSearchTerm searchTerm; - if (fullRadioButton.isSelected()) { - searchTerm = new ParentSearchTerm(parentTextField.getText(), true); - } else { - searchTerm = new ParentSearchTerm(parentTextField.getText(), false); - } + searchTerm = new ParentSearchTerm(parentTextField.getText(), fullRadioButton.isSelected(), includeRadioButton.isSelected()); parentListModel.add(parentListModel.size(), searchTerm); validateFields(); + parentTextField.setText(""); } }//GEN-LAST:event_addButtonActionPerformed @@ -1396,6 +1423,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private javax.swing.JScrollPane dataSourceScrollPane; private javax.swing.JButton deleteButton; private javax.swing.JLabel errorLabel; + private javax.swing.JRadioButton excludeRadioButton; private javax.swing.JCheckBox exifCheckbox; private javax.swing.JComboBox fileTypeComboBox; private javax.swing.JLabel fileTypeLabel; @@ -1408,6 +1436,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private javax.swing.JCheckBox hashSetCheckbox; private javax.swing.JList hashSetList; private javax.swing.JScrollPane hashSetScrollPane; + private javax.swing.JRadioButton includeRadioButton; private javax.swing.JCheckBox interestingItemsCheckbox; private javax.swing.JList interestingItemsList; private javax.swing.JScrollPane interestingItemsScrollPane; @@ -1422,10 +1451,11 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private javax.swing.JLabel orderByLabel; private javax.swing.ButtonGroup orderGroupsByButtonGroup; private javax.swing.JLabel orderGroupsByLabel; - private javax.swing.ButtonGroup parentButtonGroup; private javax.swing.JCheckBox parentCheckbox; + private javax.swing.ButtonGroup parentIncludeButtonGroup; private javax.swing.JLabel parentLabel; private javax.swing.JList parentList; + private javax.swing.ButtonGroup parentPathButtonGroup; private javax.swing.JScrollPane parentScrollPane; private javax.swing.JTextField parentTextField; private javax.swing.JCheckBox scoreCheckbox; From 0f4b08b1ee4524bf557932074eaae56fe9a07e0f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 25 Oct 2019 16:05:32 -0400 Subject: [PATCH 2/3] 5674 known status filter added --- .../autopsy/filequery/Bundle.properties | 2 + .../filequery/Bundle.properties-MERGED | 3 ++ .../filequery/FileSearchFiltering.java | 18 +++++++++ .../autopsy/filequery/FileSearchPanel.form | 35 ++++++++++++----- .../autopsy/filequery/FileSearchPanel.java | 38 ++++++++++++++----- 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 6577de6054..3d0207ef87 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -60,3 +60,5 @@ ResultsPanel.instancesList.border.title=Instances DiscoveryExtractAction.title.extractFiles.text=Extract File FileSearchPanel.includeRadioButton.text=Include FileSearchPanel.excludeRadioButton.text=Exclude +FileSearchPanel.knownFilesCheckbox.toolTipText= +FileSearchPanel.knownFilesCheckbox.text=Hide known files diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 94be3fb81b..30e742c587 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -86,6 +86,7 @@ FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0} FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0} # {0} - filters FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} +FileSearchFiltering.KnownFilter.desc=Files which are not known # {0} - filters FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0} # {0} - filters @@ -179,6 +180,8 @@ ResultsPanel.instancesList.border.title=Instances DiscoveryExtractAction.title.extractFiles.text=Extract File FileSearchPanel.includeRadioButton.text=Include FileSearchPanel.excludeRadioButton.text=Exclude +FileSearchPanel.knownFilesCheckbox.toolTipText= +FileSearchPanel.knownFilesCheckbox.text=Hide known files ResultsPanel.viewFileInDir.name=View File in Directory SearchNode.getName.text=Search Result # {0} - numberOfInstances diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index 8cf2c455e2..d0cef928f7 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -938,6 +938,24 @@ class FileSearchFiltering { } } + /** + * A filter for specifying if known files should be included. + */ + static class KnownFilter extends FileFilter { + + @Override + String getWhereClause() { + return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS + } + + @NbBundle.Messages({ + "FileSearchFiltering.KnownFilter.desc=Files which are not known"}) + @Override + String getDesc() { + return Bundle.FileSearchFiltering_KnownFilter_desc(); + } + } + @NbBundle.Messages({ "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",}) private static String concatenateSetNamesForDisplay(List setNames) { diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form index bf956d08c7..9c6bf63c21 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form @@ -172,7 +172,7 @@ - + @@ -215,7 +215,7 @@ - + @@ -231,7 +231,7 @@ - + @@ -241,7 +241,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -291,7 +291,7 @@ - + @@ -381,7 +381,7 @@ - + @@ -391,7 +391,7 @@ - + @@ -656,7 +656,7 @@ - + @@ -672,7 +672,22 @@ - + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java index 4b4865ea32..9e6008a750 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java @@ -439,6 +439,11 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener filters.add(new FileSearchFiltering.NotableFilter()); } + + if (knownFilesCheckbox.isSelected()){ + filters.add(new FileSearchFiltering.KnownFilter()); + } + if (scoreCheckbox.isSelected()) { filters.add(new FileSearchFiltering.ScoreFilter(scoreList.getSelectedValuesList())); } @@ -652,6 +657,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener scoreList = new javax.swing.JList<>(); includeRadioButton = new javax.swing.JRadioButton(); excludeRadioButton = new javax.swing.JRadioButton(); + knownFilesCheckbox = new javax.swing.JCheckBox(); fileTypeLabel = new javax.swing.JLabel(); searchButton = new javax.swing.JButton(); sortingPanel = new javax.swing.JPanel(); @@ -734,7 +740,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 11; + gridBagConstraints.gridy = 12; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); filtersPanel.add(parentCheckbox, gridBagConstraints); @@ -761,7 +767,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener fullRadioButton.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 13; + gridBagConstraints.gridy = 14; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); filtersPanel.add(fullRadioButton, gridBagConstraints); @@ -771,7 +777,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener substringRadioButton.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 13; + gridBagConstraints.gridy = 14; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.weightx = 0.5; gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); @@ -780,7 +786,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener parentTextField.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 15; + gridBagConstraints.gridy = 16; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; @@ -800,7 +806,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 15; + gridBagConstraints.gridy = 16; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 6); filtersPanel.add(addButton, gridBagConstraints); @@ -817,7 +823,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 14; + gridBagConstraints.gridy = 15; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; gridBagConstraints.insets = new java.awt.Insets(0, 10, 4, 6); filtersPanel.add(deleteButton, gridBagConstraints); @@ -871,7 +877,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener org.openide.awt.Mnemonics.setLocalizedText(parentLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.parentLabel.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 12; + gridBagConstraints.gridy = 13; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.weighty = 0.1; gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0); @@ -889,7 +895,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 11; + gridBagConstraints.gridy = 12; gridBagConstraints.gridwidth = 3; gridBagConstraints.gridheight = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; @@ -1067,7 +1073,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener includeRadioButton.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 14; + gridBagConstraints.gridy = 15; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); filtersPanel.add(includeRadioButton, gridBagConstraints); @@ -1077,12 +1083,22 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener excludeRadioButton.setEnabled(false); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 14; + gridBagConstraints.gridy = 15; gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; gridBagConstraints.weightx = 0.5; gridBagConstraints.insets = new java.awt.Insets(0, 4, 4, 0); filtersPanel.add(excludeRadioButton, gridBagConstraints); + org.openide.awt.Mnemonics.setLocalizedText(knownFilesCheckbox, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.knownFilesCheckbox.text")); // NOI18N + knownFilesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.knownFilesCheckbox.toolTipText")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 11; + gridBagConstraints.gridwidth = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 6); + filtersPanel.add(knownFilesCheckbox, gridBagConstraints); + filtersScrollPane.setViewportView(filtersPanel); org.openide.awt.Mnemonics.setLocalizedText(fileTypeLabel, org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.fileTypeLabel.text")); // NOI18N @@ -1288,6 +1304,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener scoreList.setEnabled(enabled && scoreCheckbox.isSelected()); exifCheckbox.setEnabled(enabled); notableCheckbox.setEnabled(enabled); + knownFilesCheckbox.setEnabled(enabled); parentCheckbox.setEnabled(enabled); parentScrollPane.setEnabled(enabled && parentCheckbox.isSelected()); parentList.setEnabled(enabled && parentCheckbox.isSelected()); @@ -1443,6 +1460,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private javax.swing.JCheckBox keywordCheckbox; private javax.swing.JList keywordList; private javax.swing.JScrollPane keywordScrollPane; + private javax.swing.JCheckBox knownFilesCheckbox; private javax.swing.JCheckBox notableCheckbox; private javax.swing.JCheckBox objectsCheckbox; private javax.swing.JList objectsList; From 7cce588bf36e458364eb56c6538668841ebf71c6 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 6 Nov 2019 18:06:22 -0500 Subject: [PATCH 3/3] 5674 fix codacy complaints --- .../autopsy/filequery/FileSearchFiltering.java | 16 ++++++++-------- .../autopsy/filequery/FileSearchPanel.form | 4 ++++ .../autopsy/filequery/FileSearchPanel.java | 3 +-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index d0cef928f7..66676aa669 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -235,8 +235,8 @@ class FileSearchFiltering { static class ParentSearchTerm { private final String searchStr; - private final boolean isFullPath; - private final boolean isIncluded; + private final boolean fullPath; + private final boolean included; /** * Create the ParentSearchTerm object @@ -249,8 +249,8 @@ class FileSearchFiltering { */ ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded) { this.searchStr = searchStr; - this.isFullPath = isFullPath; - this.isIncluded = isIncluded; + this.fullPath = isFullPath; + this.included = isIncluded; } /** @@ -297,17 +297,17 @@ class FileSearchFiltering { } /** - * @return the isFullPath + * @return the fullPath */ boolean isFullPath() { - return isFullPath; + return fullPath; } /** - * @return the isIncluded + * @return the included */ boolean isIncluded() { - return isIncluded; + return included; } } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form index 9c6bf63c21..5f148e7227 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form @@ -7,6 +7,10 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java index 9e6008a750..c2875ca0aa 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java @@ -614,7 +614,7 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener parentPathButtonGroup = new javax.swing.ButtonGroup(); orderGroupsByButtonGroup = new javax.swing.ButtonGroup(); - parentIncludeButtonGroup = new javax.swing.ButtonGroup(); + javax.swing.ButtonGroup parentIncludeButtonGroup = new javax.swing.ButtonGroup(); filtersScrollPane = new javax.swing.JScrollPane(); filtersPanel = new javax.swing.JPanel(); sizeCheckbox = new javax.swing.JCheckBox(); @@ -1470,7 +1470,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener private javax.swing.ButtonGroup orderGroupsByButtonGroup; private javax.swing.JLabel orderGroupsByLabel; private javax.swing.JCheckBox parentCheckbox; - private javax.swing.ButtonGroup parentIncludeButtonGroup; private javax.swing.JLabel parentLabel; private javax.swing.JList parentList; private javax.swing.ButtonGroup parentPathButtonGroup;