Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
SearchFiltering.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.discovery.search;
20 
21 import java.text.SimpleDateFormat;
29 import org.sleuthkit.datamodel.AbstractFile;
30 import org.sleuthkit.datamodel.DataSource;
31 import org.sleuthkit.datamodel.SleuthkitCase;
32 import org.sleuthkit.datamodel.TagName;
33 import org.sleuthkit.datamodel.TskCoreException;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.StringJoiner;
42 import java.util.concurrent.TimeUnit;
43 import java.util.stream.Collectors;
44 import org.openide.util.NbBundle;
45 import org.sleuthkit.datamodel.BlackboardArtifact;
46 import org.sleuthkit.datamodel.BlackboardAttribute;
47 import org.sleuthkit.datamodel.TskData;
49 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
50 
54 public class SearchFiltering {
55 
72  static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
73  if (caseDb == null) {
74  throw new DiscoveryException("Case DB parameter is null"); // NON-NLS
75  }
76  // Combine all the SQL queries from the filters into one query
77  String combinedQuery = "";
78  for (AbstractFilter filter : filters) {
79  if (!filter.getWhereClause().isEmpty()) {
80  if (!combinedQuery.isEmpty()) {
81  combinedQuery += " AND "; // NON-NLS
82  }
83  combinedQuery += "(" + filter.getWhereClause() + ")"; // NON-NLS
84  }
85  }
86 
87  if (combinedQuery.isEmpty()) {
88  // The file search filter is required, so this should never be empty.
89  throw new DiscoveryException("Selected filters do not include a case database query");
90  }
91  if (context.searchIsCancelled()) {
92  throw new SearchCancellationException("The search was cancelled before result list could be retrieved.");
93  }
94  try {
95  return getResultList(filters, combinedQuery, caseDb, centralRepoDb, context);
96  } catch (TskCoreException ex) {
97  throw new DiscoveryException("Error querying case database", ex); // NON-NLS
98  }
99  }
100 
119  private static List<Result> getResultList(List<AbstractFilter> filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws TskCoreException, DiscoveryException, SearchCancellationException {
120  // Get all matching abstract files
121  List<Result> resultList = new ArrayList<>();
122  List<AbstractFile> sqlResults = caseDb.findAllFilesWhere(combinedQuery);
123  if (context.searchIsCancelled()) {
124  throw new SearchCancellationException("The search was cancelled while the case database query was being performed.");
125  }
126  // If there are no results, return now
127  if (sqlResults.isEmpty()) {
128  return resultList;
129  }
130 
131  // Wrap each result in a ResultFile
132  for (AbstractFile abstractFile : sqlResults) {
133  resultList.add(new ResultFile(abstractFile));
134  }
135 
136  // Now run any non-SQL filters.
137  for (AbstractFilter filter : filters) {
138  if (context.searchIsCancelled()) {
139  throw new SearchCancellationException("The search was cancelled while alternate filters were being applied.");
140  }
141  if (filter.useAlternateFilter()) {
142  resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb, context);
143  }
144  // There are no matches for the filters run so far, so return
145  if (resultList.isEmpty()) {
146  return resultList;
147  }
148  }
149  return resultList;
150  }
151 
156  public static class ArtifactDateRangeFilter extends AbstractFilter {
157 
158  private final Long startDate;
159  private final Long endDate;
160 
161  // Attributes to search for date
162  private static List<BlackboardAttribute.ATTRIBUTE_TYPE> dateAttributes
163  = Arrays.asList(
164  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
165  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
166  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED
167  );
168 
175  public ArtifactDateRangeFilter(Long startDate, Long endDate) {
176  this.startDate = startDate;
177  this.endDate = endDate;
178  }
179 
184  static String createAttributeTypeClause() {
185  StringJoiner joiner = new StringJoiner(",");
186  for (BlackboardAttribute.ATTRIBUTE_TYPE type : dateAttributes) {
187  joiner.add("\'" + type.getTypeID() + "\'");
188  }
189  return "attribute_type_id IN (" + joiner.toString() + ")";
190  }
191 
192  @Override
193  public String getWhereClause() {
194  return createAttributeTypeClause()
195  + " AND (value_int64 BETWEEN " + startDate + " AND " + endDate + ")";
196  }
197 
198  @NbBundle.Messages({"SearchFiltering.dateRangeFilter.lable=Activity date ",
199  "# {0} - startDate",
200  "SearchFiltering.dateRangeFilter.after=after: {0}",
201  "# {0} - endDate",
202  "SearchFiltering.dateRangeFilter.before=before: {0}",
203  "SearchFiltering.dateRangeFilter.and= and "})
204  @Override
205  public String getDesc() {
206  String desc = ""; // NON-NLS
207  if (startDate > 0) {
208  desc += Bundle.SearchFiltering_dateRangeFilter_after(new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(startDate))));
209  }
210  if (endDate < 10000000000L) { //arbitrary time sometime in the 23rd century to check that they specified a date and the max date isn't being used
211  if (!desc.isEmpty()) {
212  desc += Bundle.SearchFiltering_dateRangeFilter_and();
213  }
214  desc += Bundle.SearchFiltering_dateRangeFilter_before(new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(endDate))));
215  }
216  if (!desc.isEmpty()) {
217  desc = Bundle.SearchFiltering_dateRangeFilter_lable() + desc;
218  }
219  return desc;
220  }
221  }
222 
226  public static class ArtifactTypeFilter extends AbstractFilter {
227 
228  private final Collection<ARTIFACT_TYPE> types;
229 
236  public ArtifactTypeFilter(Collection<ARTIFACT_TYPE> types) {
237  this.types = types;
238  }
239 
245  public Collection<ARTIFACT_TYPE> getTypes() {
246  return Collections.unmodifiableCollection(types);
247  }
248 
249  private StringJoiner joinStandardArtifactTypes() {
250  StringJoiner joiner = new StringJoiner(",");
251  for (ARTIFACT_TYPE type : types) {
252  joiner.add("\'" + type.getTypeID() + "\'");
253  }
254  return joiner;
255  }
256 
257  @Override
258  public String getWhereClause() {
259  StringJoiner joiner = joinStandardArtifactTypes();
260  return "artifact_type_id IN (" + joiner + ")";
261  }
262 
267  String getWhereClause(List<ARTIFACT_TYPE> nonVisibleArtifactTypesToInclude) {
268  StringJoiner joiner = joinStandardArtifactTypes();
269  for (ARTIFACT_TYPE type : nonVisibleArtifactTypesToInclude) {
270  joiner.add("\'" + type.getTypeID() + "\'");
271  }
272  return "artifact_type_id IN (" + joiner + ")";
273  }
274 
275  @NbBundle.Messages({"# {0} - artifactTypes",
276  "SearchFiltering.artifactTypeFilter.desc=Result type(s): {0}",
277  "SearchFiltering.artifactTypeFilter.or=, "})
278  @Override
279  public String getDesc() {
280  String desc = ""; // NON-NLS
281  for (ARTIFACT_TYPE type : types) {
282  if (!desc.isEmpty()) {
283  desc += Bundle.SearchFiltering_artifactTypeFilter_or();
284  }
285  desc += type.getDisplayName();
286  }
287  desc = Bundle.SearchFiltering_artifactTypeFilter_desc(desc);
288  return desc;
289  }
290 
291  }
292 
296  public static class SizeFilter extends AbstractFilter {
297 
298  private final List<FileSize> fileSizes;
299 
305  public SizeFilter(List<FileSize> fileSizes) {
306  this.fileSizes = fileSizes;
307  }
308 
309  @Override
310  public String getWhereClause() {
311  String queryStr = ""; // NON-NLS
312  for (FileSize size : fileSizes) {
313  if (!queryStr.isEmpty()) {
314  queryStr += " OR "; // NON-NLS
315  }
316  if (size.getMaxBytes() != FileSize.NO_MAXIMUM) {
317  queryStr += "(size > \'" + size.getMinBytes() + "\' AND size <= \'" + size.getMaxBytes() + "\')"; // NON-NLS
318  } else {
319  queryStr += "(size >= \'" + size.getMinBytes() + "\')"; // NON-NLS
320  }
321  }
322  return queryStr;
323  }
324 
325  @NbBundle.Messages({
326  "# {0} - filters",
327  "SearchFiltering.SizeFilter.desc=Size(s): {0}",
328  "SearchFiltering.SizeFilter.or=, "})
329  @Override
330  public String getDesc() {
331  String desc = ""; // NON-NLS
332  for (FileSize size : fileSizes) {
333  if (!desc.isEmpty()) {
334  desc += Bundle.SearchFiltering_SizeFilter_or();
335  }
336  desc += size.getSizeGroup();
337  }
338  desc = Bundle.SearchFiltering_SizeFilter_desc(desc);
339  return desc;
340  }
341  }
342 
347  public static class ParentSearchTerm {
348 
349  private final String searchStr;
350  private final boolean fullPath;
351  private final boolean included;
352 
362  public ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded) {
363  this.searchStr = searchStr;
364  this.fullPath = isFullPath;
365  this.included = isIncluded;
366  }
367 
373  public String getSQLForTerm() {
374  // TODO - these should really be prepared statements
375  if (isIncluded()) {
376  if (isFullPath()) {
377  return "parent_path=\'" + getSearchStr() + "\'"; // NON-NLS
378  } else {
379  return "parent_path LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS
380  }
381  } else {
382  if (isFullPath()) {
383  return "parent_path!=\'" + getSearchStr() + "\'"; // NON-NLS
384  } else {
385  return "parent_path NOT LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS
386  }
387  }
388  }
389 
390  @NbBundle.Messages({
391  "SearchFiltering.ParentSearchTerm.fullString= (exact)",
392  "SearchFiltering.ParentSearchTerm.subString= (substring)",
393  "SearchFiltering.ParentSearchTerm.includeString= (include)",
394  "SearchFiltering.ParentSearchTerm.excludeString= (exclude)",})
395  @Override
396  public String toString() {
397  String returnString = getSearchStr();
398  if (isFullPath()) {
399  returnString += Bundle.SearchFiltering_ParentSearchTerm_fullString();
400  } else {
401  returnString += Bundle.SearchFiltering_ParentSearchTerm_subString();
402  }
403  if (isIncluded()) {
404  returnString += Bundle.SearchFiltering_ParentSearchTerm_includeString();
405  } else {
406  returnString += Bundle.SearchFiltering_ParentSearchTerm_excludeString();
407  }
408  return returnString;
409  }
410 
418  public boolean isFullPath() {
419  return fullPath;
420  }
421 
429  public boolean isIncluded() {
430  return included;
431  }
432 
438  public String getSearchStr() {
439  return searchStr;
440  }
441  }
442 
446  public static class ParentFilter extends AbstractFilter {
447 
448  private final List<ParentSearchTerm> parentSearchTerms;
449 
455  public ParentFilter(List<ParentSearchTerm> parentSearchTerms) {
456  this.parentSearchTerms = parentSearchTerms;
457  }
458 
459  @Override
460  public String getWhereClause() {
461  String includeQueryStr = ""; // NON-NLS
462  String excludeQueryStr = "";
463  for (ParentSearchTerm searchTerm : parentSearchTerms) {
464  if (searchTerm.isIncluded()) {
465  if (!includeQueryStr.isEmpty()) {
466  includeQueryStr += " OR "; // NON-NLS
467  }
468  includeQueryStr += searchTerm.getSQLForTerm();
469  } else {
470  if (!excludeQueryStr.isEmpty()) {
471  excludeQueryStr += " AND "; // NON-NLS
472  }
473  excludeQueryStr += searchTerm.getSQLForTerm();
474  }
475  }
476  if (!includeQueryStr.isEmpty()) {
477  includeQueryStr = "(" + includeQueryStr + ")";
478  }
479  if (!excludeQueryStr.isEmpty()) {
480  excludeQueryStr = "(" + excludeQueryStr + ")";
481  }
482  if (includeQueryStr.isEmpty() || excludeQueryStr.isEmpty()) {
483  return includeQueryStr + excludeQueryStr;
484  } else {
485  return includeQueryStr + " AND " + excludeQueryStr;
486  }
487  }
488 
489  @NbBundle.Messages({
490  "# {0} - filters",
491  "SearchFiltering.ParentFilter.desc=Paths matching: {0}",
492  "SearchFiltering.ParentFilter.or=, ",
493  "SearchFiltering.ParentFilter.exact=(exact match)",
494  "SearchFiltering.ParentFilter.substring=(substring)",
495  "SearchFiltering.ParentFilter.included=(included)",
496  "SearchFiltering.ParentFilter.excluded=(excluded)"})
497  @Override
498  public String getDesc() {
499  String desc = ""; // NON-NLS
500  for (ParentSearchTerm searchTerm : parentSearchTerms) {
501  if (!desc.isEmpty()) {
502  desc += Bundle.SearchFiltering_ParentFilter_or();
503  }
504  if (searchTerm.isFullPath()) {
505  desc += searchTerm.getSearchStr() + Bundle.SearchFiltering_ParentFilter_exact();
506  } else {
507  desc += searchTerm.getSearchStr() + Bundle.SearchFiltering_ParentFilter_substring();
508  }
509  if (searchTerm.isIncluded()) {
510  desc += Bundle.SearchFiltering_ParentFilter_included();
511  } else {
512  desc += Bundle.SearchFiltering_ParentFilter_excluded();
513  }
514  }
515  desc = Bundle.SearchFiltering_ParentFilter_desc(desc);
516  return desc;
517  }
518  }
519 
523  public static class DataSourceFilter extends AbstractFilter {
524 
525  private final List<DataSource> dataSources;
526 
532  public DataSourceFilter(List<DataSource> dataSources) {
533  this.dataSources = dataSources;
534  }
535 
536  @Override
537  public String getWhereClause() {
538  String queryStr = ""; // NON-NLS
539  for (DataSource ds : dataSources) {
540  if (!queryStr.isEmpty()) {
541  queryStr += ","; // NON-NLS
542  }
543  queryStr += "\'" + ds.getId() + "\'"; // NON-NLS
544  }
545  queryStr = "data_source_obj_id IN (" + queryStr + ")"; // NON-NLS
546  return queryStr;
547  }
548 
549  @NbBundle.Messages({
550  "# {0} - filters",
551  "SearchFiltering.DataSourceFilter.desc=Data source(s): {0}",
552  "SearchFiltering.DataSourceFilter.or=, ",
553  "# {0} - Data source name",
554  "# {1} - Data source ID",
555  "SearchFiltering.DataSourceFilter.datasource={0}({1})",})
556  @Override
557  public String getDesc() {
558  String desc = ""; // NON-NLS
559  for (DataSource ds : dataSources) {
560  if (!desc.isEmpty()) {
561  desc += Bundle.SearchFiltering_DataSourceFilter_or();
562  }
563  desc += Bundle.SearchFiltering_DataSourceFilter_datasource(ds.getName(), ds.getId());
564  }
565  desc = Bundle.SearchFiltering_DataSourceFilter_desc(desc);
566  return desc;
567  }
568  }
569 
574  public static class KeywordListFilter extends AbstractFilter {
575 
576  private final List<String> listNames;
577 
583  public KeywordListFilter(List<String> listNames) {
584  this.listNames = listNames;
585  }
586 
587  @Override
588  public String getWhereClause() {
589  String keywordListPart = concatenateNamesForSQL(listNames);
590 
591  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
592  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = 9 AND attribute_type_ID = 37 "
593  + "AND (" + keywordListPart + "))))"; // NON-NLS
594 
595  return queryStr;
596  }
597 
598  @NbBundle.Messages({
599  "# {0} - filters",
600  "SearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}",})
601  @Override
602  public String getDesc() {
603  return Bundle.SearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames));
604  }
605  }
606 
610  public static class FileTypeFilter extends AbstractFilter {
611 
612  private final List<Type> categories;
613 
619  public FileTypeFilter(List<Type> categories) {
620  this.categories = categories;
621  }
622 
628  public FileTypeFilter(Type category) {
629  this.categories = new ArrayList<>();
630  this.categories.add(category);
631  }
632 
633  @Override
634  public String getWhereClause() {
635  String queryStr = ""; // NON-NLS
636  for (Type cat : categories) {
637  for (String type : cat.getMediaTypes()) {
638  if (!queryStr.isEmpty()) {
639  queryStr += ","; // NON-NLS
640  }
641  queryStr += "\'" + type + "\'"; // NON-NLS
642  }
643  }
644  queryStr = "mime_type IN (" + queryStr + ")"; // NON-NLS
645  return queryStr;
646  }
647 
648  @NbBundle.Messages({
649  "# {0} - filters",
650  "SearchFiltering.FileTypeFilter.desc=Type: {0}",
651  "SearchFiltering.FileTypeFilter.or=, ",})
652  @Override
653  public String getDesc() {
654  String desc = "";
655  for (Type cat : categories) {
656  if (!desc.isEmpty()) {
657  desc += Bundle.SearchFiltering_FileTypeFilter_or();
658  }
659  desc += cat.toString();
660  }
661  desc = Bundle.SearchFiltering_FileTypeFilter_desc(desc);
662  return desc;
663  }
664  }
665 
669  public static class FrequencyFilter extends AbstractFilter {
670 
671  private final List<Frequency> frequencies;
672 
678  public FrequencyFilter(List<Frequency> frequencies) {
679  this.frequencies = frequencies;
680  }
681 
682  @Override
683  public String getWhereClause() {
684  // Since this relies on the central repository database, there is no
685  // query on the case database.
686  return ""; // NON-NLS
687  }
688 
689  @Override
690  public boolean useAlternateFilter() {
691  return true;
692  }
693 
694  @Override
695  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
696  CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
697  // Set the frequency for each file
699  freqAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb, context);
700 
701  // If the frequency matches the filter, add the file to the results
702  List<Result> frequencyResults = new ArrayList<>();
703  for (Result file : currentResults) {
704  if (context.searchIsCancelled()) {
705  throw new SearchCancellationException("The search was cancelled while Frequency alternate filter was being applied.");
706  }
707  if (frequencies.contains(file.getFrequency())) {
708  frequencyResults.add(file);
709  }
710  }
711  return frequencyResults;
712  }
713 
714  @NbBundle.Messages({
715  "# {0} - filters",
716  "SearchFiltering.FrequencyFilter.desc=Past occurrences: {0}",
717  "SearchFiltering.FrequencyFilter.or=, ",})
718  @Override
719  public String getDesc() {
720  String desc = ""; // NON-NLS
721  for (Frequency freq : frequencies) {
722  if (!desc.isEmpty()) {
723  desc += Bundle.SearchFiltering_FrequencyFilter_or();
724  }
725  desc += freq.toString();
726  }
727  return Bundle.SearchFiltering_FrequencyFilter_desc(desc);
728  }
729  }
730 
734  public static class KnownAccountTypeFilter extends AbstractFilter {
735 
736  @Override
737  public String getWhereClause() {
738  throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
739  }
740 
741  @Override
742  public boolean useAlternateFilter() {
743  return true;
744  }
745 
746  @Override
747  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
748  CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
749  List<Result> filteredResults = new ArrayList<>();
750  for (Result result : currentResults) {
751  if (context.searchIsCancelled()) {
752  throw new SearchCancellationException("The search was cancelled while Known Account Type alternate filter was being applied.");
753  }
754  if (result instanceof ResultDomain) {
755  ResultDomain domain = (ResultDomain) result;
756  if (domain.hasKnownAccountType()) {
757  filteredResults.add(domain);
758  }
759  } else {
760  filteredResults.add(result);
761  }
762  }
763  return filteredResults;
764  }
765 
766  @NbBundle.Messages({
767  "SearchFiltering.KnownAccountTypeFilter.desc=Only domains with known account type"
768  })
769  @Override
770  public String getDesc() {
771  return Bundle.SearchFiltering_KnownAccountTypeFilter_desc();
772  }
773 
774  }
775 
779  public static class PreviouslyNotableFilter extends AbstractFilter {
780 
781  @Override
782  public String getWhereClause() {
783  throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
784  }
785 
786  @Override
787  public boolean useAlternateFilter() {
788  return true;
789  }
790 
791  @Override
792  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
793  CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
795  previouslyNotableAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb, context);
796  List<Result> filteredResults = new ArrayList<>();
797  for (Result file : currentResults) {
798  if (context.searchIsCancelled()) {
799  throw new SearchCancellationException("The search was cancelled while Previously Notable alternate filter was being applied.");
800  }
801  if (file.getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
802  filteredResults.add(file);
803  }
804  }
805  return filteredResults;
806  }
807 
808  @NbBundle.Messages({
809  "SearchFiltering.PreviouslyNotableFilter.desc=Previously marked as notable in central repository"
810  })
811  @Override
812  public String getDesc() {
813  return Bundle.SearchFiltering_PreviouslyNotableFilter_desc();
814  }
815 
816  }
817 
822  public static class HashSetFilter extends AbstractFilter {
823 
824  private final List<String> setNames;
825 
831  public HashSetFilter(List<String> setNames) {
832  this.setNames = setNames;
833  }
834 
835  @Override
836  public String getWhereClause() {
837  String hashSetPart = concatenateNamesForSQL(setNames);
838 
839  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
840  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
841  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
842  + "AND (" + hashSetPart + "))))"; // NON-NLS
843 
844  return queryStr;
845  }
846 
847  @NbBundle.Messages({
848  "# {0} - filters",
849  "FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}",})
850  @Override
851  public String getDesc() {
852  return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames));
853  }
854  }
855 
860  public static class InterestingFileSetFilter extends AbstractFilter {
861 
862  private final List<String> setNames;
863 
869  public InterestingFileSetFilter(List<String> setNames) {
870  this.setNames = setNames;
871  }
872 
873  @Override
874  public String getWhereClause() {
875  String intItemSetPart = concatenateNamesForSQL(setNames);
876 
877  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
878  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
879  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
880  + "AND (" + intItemSetPart + "))))"; // NON-NLS
881 
882  return queryStr;
883  }
884 
885  @NbBundle.Messages({
886  "# {0} - filters",
887  "SearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",})
888  @Override
889  public String getDesc() {
890  return Bundle.SearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames));
891  }
892  }
893 
898  public static class ObjectDetectionFilter extends AbstractFilter {
899 
900  private final List<String> typeNames;
901 
907  public ObjectDetectionFilter(List<String> typeNames) {
908  this.typeNames = typeNames;
909  }
910 
911  @Override
912  public String getWhereClause() {
913  String objTypePart = concatenateNamesForSQL(typeNames);
914 
915  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
916  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
917  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " "
918  + "AND (" + objTypePart + "))))"; // NON-NLS
919 
920  return queryStr;
921  }
922 
923  @NbBundle.Messages({
924  "# {0} - filters",
925  "SearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",})
926  @Override
927  public String getDesc() {
928  return Bundle.SearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames));
929  }
930  }
931 
936  public static class ScoreFilter extends AbstractFilter {
937 
938  private final List<Score> scores;
939 
945  public ScoreFilter(List<Score> scores) {
946  this.scores = scores;
947  }
948 
949  @Override
950  public String getWhereClause() {
951 
952  // Current algorithm:
953  // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag.
954  // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag.
955  String hashsetQueryPart = "";
956  String tagQueryPart = "";
957  String intItemQueryPart = "";
958 
959  if (scores.contains(Score.NOTABLE)) {
960  // do hashset
961  hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") ";
962  }
963 
964  if (scores.contains(Score.INTERESTING)) {
965  // Matches interesting item artifact
966  intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = "
967  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) ";
968  }
969 
970  if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) {
971  // Any tag will work
972  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))";
973  } else if (scores.contains(Score.NOTABLE)) {
974  // Notable tags
975  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = "
976  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
977  } else if (scores.contains(Score.INTERESTING)) {
978  // Non-notable tags
979  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != "
980  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
981  }
982 
983  String queryStr = hashsetQueryPart;
984  if (!intItemQueryPart.isEmpty()) {
985  if (!queryStr.isEmpty()) {
986  queryStr += " OR ";
987  }
988  queryStr += intItemQueryPart;
989  }
990  if (!tagQueryPart.isEmpty()) {
991  if (!queryStr.isEmpty()) {
992  queryStr += " OR ";
993  }
994  queryStr += tagQueryPart;
995  }
996  return queryStr;
997  }
998 
999  @NbBundle.Messages({
1000  "# {0} - filters",
1001  "SearchFiltering.ScoreFilter.desc=Score(s) of : {0}",})
1002  @Override
1003  public String getDesc() {
1004  return Bundle.SearchFiltering_ScoreFilter_desc(
1005  concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList())));
1006  }
1007  }
1008 
1013  public static class TagsFilter extends AbstractFilter {
1014 
1015  private final List<TagName> tagNames;
1016 
1022  public TagsFilter(List<TagName> tagNames) {
1023  this.tagNames = tagNames;
1024  }
1025 
1026  @Override
1027  public String getWhereClause() {
1028  String tagIDs = ""; // NON-NLS
1029  for (TagName tagName : tagNames) {
1030  if (!tagIDs.isEmpty()) {
1031  tagIDs += ",";
1032  }
1033  tagIDs += tagName.getId();
1034  }
1035 
1036  String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))";
1037 
1038  return queryStr;
1039  }
1040 
1041  @NbBundle.Messages({
1042  "# {0} - tag names",
1043  "FileSearchFiltering.TagsFilter.desc=Tagged {0}",
1044  "FileSearchFiltering.TagsFilter.or=, ",})
1045  @Override
1046  public String getDesc() {
1047  String desc = ""; // NON-NLS
1048  for (TagName name : tagNames) {
1049  if (!desc.isEmpty()) {
1050  desc += Bundle.FileSearchFiltering_TagsFilter_or();
1051  }
1052  desc += name.getDisplayName();
1053  }
1054  return Bundle.FileSearchFiltering_TagsFilter_desc(desc);
1055  }
1056  }
1057 
1062  public static class UserCreatedFilter extends AbstractFilter {
1063 
1064  @Override
1065  public String getWhereClause() {
1066  return "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
1067  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = "
1068  + BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID() + ")))";
1069  }
1070 
1071  @NbBundle.Messages({
1072  "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",})
1073  @Override
1074  public String getDesc() {
1075  return Bundle.FileSearchFiltering_UserCreatedFilter_desc();
1076  }
1077  }
1078 
1083  public static class NotableFilter extends AbstractFilter {
1084 
1085  @Override
1086  public String getWhereClause() {
1087  // Since this relies on the central repository database, there is no
1088  // query on the case database.
1089  return ""; // NON-NLS
1090  }
1091 
1092  @Override
1093  public boolean useAlternateFilter() {
1094  return true;
1095  }
1096 
1097  @Override
1098  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
1099  CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
1100 
1101  if (centralRepoDb == null) {
1102  throw new DiscoveryException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS
1103  }
1104 
1105  // We have to have run some kind of SQL filter before getting to this point,
1106  // and should have checked afterward to see if the results were empty.
1107  if (currentResults.isEmpty()) {
1108  throw new DiscoveryException("Can not run on empty list"); // NON-NLS
1109  }
1110 
1111  // The matching files
1112  List<Result> notableResults = new ArrayList<>();
1113 
1114  try {
1116 
1117  for (Result result : currentResults) {
1118  if (context.searchIsCancelled()) {
1119  throw new SearchCancellationException("The search was cancelled while Notable alternate filter was being applied.");
1120  }
1121  ResultFile file = (ResultFile) result;
1122  if (result.getType() == SearchData.Type.DOMAIN) {
1123  break;
1124  }
1125  if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) {
1126  // Check if this file hash is marked as notable in the CR
1127  String value = file.getFirstInstance().getMd5Hash();
1128  if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) {
1129  notableResults.add(result);
1130  }
1131  }
1132  }
1133  return notableResults;
1135  throw new DiscoveryException("Error querying central repository", ex); // NON-NLS
1136  }
1137  }
1138 
1139  @NbBundle.Messages({
1140  "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",})
1141  @Override
1142  public String getDesc() {
1143  return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc();
1144  }
1145  }
1146 
1150  public static class KnownFilter extends AbstractFilter {
1151 
1152  @Override
1153  public String getWhereClause() {
1154  return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS
1155  }
1156 
1157  @NbBundle.Messages({
1158  "FileSearchFiltering.KnownFilter.desc=which are not known"})
1159  @Override
1160  public String getDesc() {
1161  return Bundle.FileSearchFiltering_KnownFilter_desc();
1162  }
1163  }
1164 
1172  @NbBundle.Messages({
1173  "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",})
1174  private static String concatenateSetNamesForDisplay(List<String> setNames) {
1175  String desc = ""; // NON-NLS
1176  for (String setName : setNames) {
1177  if (!desc.isEmpty()) {
1178  desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma();
1179  }
1180  desc += setName;
1181  }
1182  return desc;
1183  }
1184 
1193  private static String concatenateNamesForSQL(List<String> setNames) {
1194  String result = ""; // NON-NLS
1195  for (String setName : setNames) {
1196  if (!result.isEmpty()) {
1197  result += " OR "; // NON-NLS
1198  }
1199  result += "value_text = \'" + setName + "\'"; // NON-NLS
1200  }
1201  return result;
1202  }
1203 
1207  private SearchFiltering() {
1208  // Class should not be instantiated
1209  }
1210 }
static String concatenateNamesForSQL(List< String > setNames)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value)
static String concatenateSetNamesForDisplay(List< String > setNames)
static List< Result > getResultList(List< AbstractFilter > filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.