Autopsy  4.19.3
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 
877  @SuppressWarnings("deprecation")
878  @Override
879  public String getWhereClause() {
880  String intItemSetPart = concatenateNamesForSQL(setNames);
881 
882  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
883  + "(SELECT artifact_id FROM blackboard_attributes WHERE (artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
884  + " OR artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID()
885  + ") AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
886  + "AND (" + intItemSetPart + "))))"; // NON-NLS
887 
888  return queryStr;
889  }
890 
891  @NbBundle.Messages({
892  "# {0} - filters",
893  "SearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",})
894  @Override
895  public String getDesc() {
896  return Bundle.SearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames));
897  }
898  }
899 
904  public static class ObjectDetectionFilter extends AbstractFilter {
905 
906  private final List<String> typeNames;
907 
913  public ObjectDetectionFilter(List<String> typeNames) {
914  this.typeNames = typeNames;
915  }
916 
917  @Override
918  public String getWhereClause() {
919  String objTypePart = concatenateNamesForSQL(typeNames);
920 
921  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
922  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
923  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " "
924  + "AND (" + objTypePart + "))))"; // NON-NLS
925 
926  return queryStr;
927  }
928 
929  @NbBundle.Messages({
930  "# {0} - filters",
931  "SearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",})
932  @Override
933  public String getDesc() {
934  return Bundle.SearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames));
935  }
936  }
937 
942  public static class ScoreFilter extends AbstractFilter {
943 
944  private final List<Score> scores;
945 
951  public ScoreFilter(List<Score> scores) {
952  this.scores = scores;
953  }
954 
959  @SuppressWarnings("deprecation")
960  @Override
961  public String getWhereClause() {
962 
963  // Current algorithm:
964  // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag.
965  // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag.
966  String hashsetQueryPart = "";
967  String tagQueryPart = "";
968  String intItemQueryPart = "";
969 
970  if (scores.contains(Score.NOTABLE)) {
971  // do hashset
972  hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") ";
973  }
974 
975  if (scores.contains(Score.INTERESTING)) {
976  // Matches interesting item artifact
977  intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = "
978  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID() + " OR artifact_type_id = "
979  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) ";
980  }
981 
982  if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) {
983  // Any tag will work
984  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))";
985  } else if (scores.contains(Score.NOTABLE)) {
986  // Notable tags
987  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = "
988  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
989  } else if (scores.contains(Score.INTERESTING)) {
990  // Non-notable tags
991  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != "
992  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
993  }
994 
995  String queryStr = hashsetQueryPart;
996  if (!intItemQueryPart.isEmpty()) {
997  if (!queryStr.isEmpty()) {
998  queryStr += " OR ";
999  }
1000  queryStr += intItemQueryPart;
1001  }
1002  if (!tagQueryPart.isEmpty()) {
1003  if (!queryStr.isEmpty()) {
1004  queryStr += " OR ";
1005  }
1006  queryStr += tagQueryPart;
1007  }
1008  return queryStr;
1009  }
1010 
1011  @NbBundle.Messages({
1012  "# {0} - filters",
1013  "SearchFiltering.ScoreFilter.desc=Score(s) of : {0}",})
1014  @Override
1015  public String getDesc() {
1016  return Bundle.SearchFiltering_ScoreFilter_desc(
1017  concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList())));
1018  }
1019  }
1020 
1025  public static class TagsFilter extends AbstractFilter {
1026 
1027  private final List<TagName> tagNames;
1028 
1034  public TagsFilter(List<TagName> tagNames) {
1035  this.tagNames = tagNames;
1036  }
1037 
1038  @Override
1039  public String getWhereClause() {
1040  String tagIDs = ""; // NON-NLS
1041  for (TagName tagName : tagNames) {
1042  if (!tagIDs.isEmpty()) {
1043  tagIDs += ",";
1044  }
1045  tagIDs += tagName.getId();
1046  }
1047 
1048  String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))";
1049 
1050  return queryStr;
1051  }
1052 
1053  @NbBundle.Messages({
1054  "# {0} - tag names",
1055  "FileSearchFiltering.TagsFilter.desc=Tagged {0}",
1056  "FileSearchFiltering.TagsFilter.or=, ",})
1057  @Override
1058  public String getDesc() {
1059  String desc = ""; // NON-NLS
1060  for (TagName name : tagNames) {
1061  if (!desc.isEmpty()) {
1062  desc += Bundle.FileSearchFiltering_TagsFilter_or();
1063  }
1064  desc += name.getDisplayName();
1065  }
1066  return Bundle.FileSearchFiltering_TagsFilter_desc(desc);
1067  }
1068  }
1069 
1074  public static class UserCreatedFilter extends AbstractFilter {
1075 
1076  @Override
1077  public String getWhereClause() {
1078  return "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
1079  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = "
1080  + BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID() + ")))";
1081  }
1082 
1083  @NbBundle.Messages({
1084  "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",})
1085  @Override
1086  public String getDesc() {
1087  return Bundle.FileSearchFiltering_UserCreatedFilter_desc();
1088  }
1089  }
1090 
1095  public static class NotableFilter extends AbstractFilter {
1096 
1097  @Override
1098  public String getWhereClause() {
1099  // Since this relies on the central repository database, there is no
1100  // query on the case database.
1101  return ""; // NON-NLS
1102  }
1103 
1104  @Override
1105  public boolean useAlternateFilter() {
1106  return true;
1107  }
1108 
1109  @Override
1110  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
1111  CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
1112 
1113  if (centralRepoDb == null) {
1114  throw new DiscoveryException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS
1115  }
1116 
1117  // We have to have run some kind of SQL filter before getting to this point,
1118  // and should have checked afterward to see if the results were empty.
1119  if (currentResults.isEmpty()) {
1120  throw new DiscoveryException("Can not run on empty list"); // NON-NLS
1121  }
1122 
1123  // The matching files
1124  List<Result> notableResults = new ArrayList<>();
1125 
1126  try {
1128 
1129  for (Result result : currentResults) {
1130  if (context.searchIsCancelled()) {
1131  throw new SearchCancellationException("The search was cancelled while Notable alternate filter was being applied.");
1132  }
1133  ResultFile file = (ResultFile) result;
1134  if (result.getType() == SearchData.Type.DOMAIN) {
1135  break;
1136  }
1137  if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) {
1138  // Check if this file hash is marked as notable in the CR
1139  String value = file.getFirstInstance().getMd5Hash();
1140  if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) {
1141  notableResults.add(result);
1142  }
1143  }
1144  }
1145  return notableResults;
1147  throw new DiscoveryException("Error querying central repository", ex); // NON-NLS
1148  }
1149  }
1150 
1151  @NbBundle.Messages({
1152  "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",})
1153  @Override
1154  public String getDesc() {
1155  return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc();
1156  }
1157  }
1158 
1162  public static class KnownFilter extends AbstractFilter {
1163 
1164  @Override
1165  public String getWhereClause() {
1166  return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS
1167  }
1168 
1169  @NbBundle.Messages({
1170  "FileSearchFiltering.KnownFilter.desc=which are not known"})
1171  @Override
1172  public String getDesc() {
1173  return Bundle.FileSearchFiltering_KnownFilter_desc();
1174  }
1175  }
1176 
1184  @NbBundle.Messages({
1185  "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",})
1186  private static String concatenateSetNamesForDisplay(List<String> setNames) {
1187  String desc = ""; // NON-NLS
1188  for (String setName : setNames) {
1189  if (!desc.isEmpty()) {
1190  desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma();
1191  }
1192  desc += setName;
1193  }
1194  return desc;
1195  }
1196 
1205  private static String concatenateNamesForSQL(List<String> setNames) {
1206  String result = ""; // NON-NLS
1207  for (String setName : setNames) {
1208  if (!result.isEmpty()) {
1209  result += " OR "; // NON-NLS
1210  }
1211  result += "value_text = \'" + setName + "\'"; // NON-NLS
1212  }
1213  return result;
1214  }
1215 
1219  private SearchFiltering() {
1220  // Class should not be instantiated
1221  }
1222 }
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-2022 Basis Technology. Generated on: Fri Dec 2 2022
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.