Autopsy  4.18.0
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-2020 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 
66  static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
67  if (caseDb == null) {
68  throw new DiscoveryException("Case DB parameter is null"); // NON-NLS
69  }
70  // Combine all the SQL queries from the filters into one query
71  String combinedQuery = "";
72  for (AbstractFilter filter : filters) {
73  if (!filter.getWhereClause().isEmpty()) {
74  if (!combinedQuery.isEmpty()) {
75  combinedQuery += " AND "; // NON-NLS
76  }
77  combinedQuery += "(" + filter.getWhereClause() + ")"; // NON-NLS
78  }
79  }
80 
81  if (combinedQuery.isEmpty()) {
82  // The file search filter is required, so this should never be empty.
83  throw new DiscoveryException("Selected filters do not include a case database query");
84  }
85  try {
86  return getResultList(filters, combinedQuery, caseDb, centralRepoDb);
87  } catch (TskCoreException ex) {
88  throw new DiscoveryException("Error querying case database", ex); // NON-NLS
89  }
90  }
91 
106  private static List<Result> getResultList(List<AbstractFilter> filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws TskCoreException, DiscoveryException {
107  // Get all matching abstract files
108  List<Result> resultList = new ArrayList<>();
109  List<AbstractFile> sqlResults = caseDb.findAllFilesWhere(combinedQuery);
110 
111  // If there are no results, return now
112  if (sqlResults.isEmpty()) {
113  return resultList;
114  }
115 
116  // Wrap each result in a ResultFile
117  for (AbstractFile abstractFile : sqlResults) {
118  resultList.add(new ResultFile(abstractFile));
119  }
120 
121  // Now run any non-SQL filters.
122  for (AbstractFilter filter : filters) {
123  if (filter.useAlternateFilter()) {
124  resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb);
125  }
126  // There are no matches for the filters run so far, so return
127  if (resultList.isEmpty()) {
128  return resultList;
129  }
130  }
131  return resultList;
132  }
133 
138  public static class ArtifactDateRangeFilter extends AbstractFilter {
139 
140  private final Long startDate;
141  private final Long endDate;
142 
143  // Attributes to search for date
144  private static List<BlackboardAttribute.ATTRIBUTE_TYPE> dateAttributes
145  = Arrays.asList(
146  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
147  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
148  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED
149  );
150 
157  public ArtifactDateRangeFilter(Long startDate, Long endDate) {
158  this.startDate = startDate;
159  this.endDate = endDate;
160  }
161 
166  static String createAttributeTypeClause() {
167  StringJoiner joiner = new StringJoiner(",");
168  for (BlackboardAttribute.ATTRIBUTE_TYPE type : dateAttributes) {
169  joiner.add("\'" + type.getTypeID() + "\'");
170  }
171  return "attribute_type_id IN (" + joiner.toString() + ")";
172  }
173 
174  @Override
175  public String getWhereClause() {
176  return createAttributeTypeClause()
177  + " AND (value_int64 BETWEEN " + startDate + " AND " + endDate + ")";
178  }
179 
180  @NbBundle.Messages({"SearchFiltering.dateRangeFilter.lable=Activity date ",
181  "# {0} - startDate",
182  "SearchFiltering.dateRangeFilter.after=after: {0}",
183  "# {0} - endDate",
184  "SearchFiltering.dateRangeFilter.before=before: {0}",
185  "SearchFiltering.dateRangeFilter.and= and "})
186  @Override
187  public String getDesc() {
188  String desc = ""; // NON-NLS
189  if (startDate > 0) {
190  desc += Bundle.SearchFiltering_dateRangeFilter_after(new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(startDate))));
191  }
192  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
193  if (!desc.isEmpty()) {
194  desc += Bundle.SearchFiltering_dateRangeFilter_and();
195  }
196  desc += Bundle.SearchFiltering_dateRangeFilter_before(new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(endDate))));
197  }
198  if (!desc.isEmpty()) {
199  desc = Bundle.SearchFiltering_dateRangeFilter_lable() + desc;
200  }
201  return desc;
202  }
203  }
204 
208  public static class ArtifactTypeFilter extends AbstractFilter {
209 
210  private final Collection<ARTIFACT_TYPE> types;
211 
218  public ArtifactTypeFilter(Collection<ARTIFACT_TYPE> types) {
219  this.types = types;
220  }
221 
227  public Collection<ARTIFACT_TYPE> getTypes() {
228  return Collections.unmodifiableCollection(types);
229  }
230 
231  private StringJoiner joinStandardArtifactTypes() {
232  StringJoiner joiner = new StringJoiner(",");
233  for (ARTIFACT_TYPE type : types) {
234  joiner.add("\'" + type.getTypeID() + "\'");
235  }
236  return joiner;
237  }
238 
239  @Override
240  public String getWhereClause() {
241  StringJoiner joiner = joinStandardArtifactTypes();
242  return "artifact_type_id IN (" + joiner + ")";
243  }
244 
248  String getWhereClause(List<ARTIFACT_TYPE> nonVisibleArtifactTypesToInclude) {
249  StringJoiner joiner = joinStandardArtifactTypes();
250  for (ARTIFACT_TYPE type : nonVisibleArtifactTypesToInclude) {
251  joiner.add("\'" + type.getTypeID() + "\'");
252  }
253  return "artifact_type_id IN (" + joiner + ")";
254  }
255 
256  @NbBundle.Messages({"# {0} - artifactTypes",
257  "SearchFiltering.artifactTypeFilter.desc=Result type(s): {0}",
258  "SearchFiltering.artifactTypeFilter.or=, "})
259  @Override
260  public String getDesc() {
261  String desc = ""; // NON-NLS
262  for (ARTIFACT_TYPE type : types) {
263  if (!desc.isEmpty()) {
264  desc += Bundle.SearchFiltering_artifactTypeFilter_or();
265  }
266  desc += type.getDisplayName();
267  }
268  desc = Bundle.SearchFiltering_artifactTypeFilter_desc(desc);
269  return desc;
270  }
271 
272  }
273 
277  public static class SizeFilter extends AbstractFilter {
278 
279  private final List<FileSize> fileSizes;
280 
286  public SizeFilter(List<FileSize> fileSizes) {
287  this.fileSizes = fileSizes;
288  }
289 
290  @Override
291  public String getWhereClause() {
292  String queryStr = ""; // NON-NLS
293  for (FileSize size : fileSizes) {
294  if (!queryStr.isEmpty()) {
295  queryStr += " OR "; // NON-NLS
296  }
297  if (size.getMaxBytes() != FileSize.NO_MAXIMUM) {
298  queryStr += "(size > \'" + size.getMinBytes() + "\' AND size <= \'" + size.getMaxBytes() + "\')"; // NON-NLS
299  } else {
300  queryStr += "(size >= \'" + size.getMinBytes() + "\')"; // NON-NLS
301  }
302  }
303  return queryStr;
304  }
305 
306  @NbBundle.Messages({
307  "# {0} - filters",
308  "SearchFiltering.SizeFilter.desc=Size(s): {0}",
309  "SearchFiltering.SizeFilter.or=, "})
310  @Override
311  public String getDesc() {
312  String desc = ""; // NON-NLS
313  for (FileSize size : fileSizes) {
314  if (!desc.isEmpty()) {
315  desc += Bundle.SearchFiltering_SizeFilter_or();
316  }
317  desc += size.getSizeGroup();
318  }
319  desc = Bundle.SearchFiltering_SizeFilter_desc(desc);
320  return desc;
321  }
322  }
323 
328  public static class ParentSearchTerm {
329 
330  private final String searchStr;
331  private final boolean fullPath;
332  private final boolean included;
333 
343  public ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded) {
344  this.searchStr = searchStr;
345  this.fullPath = isFullPath;
346  this.included = isIncluded;
347  }
348 
354  public String getSQLForTerm() {
355  // TODO - these should really be prepared statements
356  if (isIncluded()) {
357  if (isFullPath()) {
358  return "parent_path=\'" + getSearchStr() + "\'"; // NON-NLS
359  } else {
360  return "parent_path LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS
361  }
362  } else {
363  if (isFullPath()) {
364  return "parent_path!=\'" + getSearchStr() + "\'"; // NON-NLS
365  } else {
366  return "parent_path NOT LIKE \'%" + getSearchStr() + "%\'"; // NON-NLS
367  }
368  }
369  }
370 
371  @NbBundle.Messages({
372  "SearchFiltering.ParentSearchTerm.fullString= (exact)",
373  "SearchFiltering.ParentSearchTerm.subString= (substring)",
374  "SearchFiltering.ParentSearchTerm.includeString= (include)",
375  "SearchFiltering.ParentSearchTerm.excludeString= (exclude)",})
376  @Override
377  public String toString() {
378  String returnString = getSearchStr();
379  if (isFullPath()) {
380  returnString += Bundle.SearchFiltering_ParentSearchTerm_fullString();
381  } else {
382  returnString += Bundle.SearchFiltering_ParentSearchTerm_subString();
383  }
384  if (isIncluded()) {
385  returnString += Bundle.SearchFiltering_ParentSearchTerm_includeString();
386  } else {
387  returnString += Bundle.SearchFiltering_ParentSearchTerm_excludeString();
388  }
389  return returnString;
390  }
391 
399  public boolean isFullPath() {
400  return fullPath;
401  }
402 
410  public boolean isIncluded() {
411  return included;
412  }
413 
419  public String getSearchStr() {
420  return searchStr;
421  }
422  }
423 
427  public static class ParentFilter extends AbstractFilter {
428 
429  private final List<ParentSearchTerm> parentSearchTerms;
430 
436  public ParentFilter(List<ParentSearchTerm> parentSearchTerms) {
437  this.parentSearchTerms = parentSearchTerms;
438  }
439 
440  @Override
441  public String getWhereClause() {
442  String includeQueryStr = ""; // NON-NLS
443  String excludeQueryStr = "";
444  for (ParentSearchTerm searchTerm : parentSearchTerms) {
445  if (searchTerm.isIncluded()) {
446  if (!includeQueryStr.isEmpty()) {
447  includeQueryStr += " OR "; // NON-NLS
448  }
449  includeQueryStr += searchTerm.getSQLForTerm();
450  } else {
451  if (!excludeQueryStr.isEmpty()) {
452  excludeQueryStr += " AND "; // NON-NLS
453  }
454  excludeQueryStr += searchTerm.getSQLForTerm();
455  }
456  }
457  if (!includeQueryStr.isEmpty()) {
458  includeQueryStr = "(" + includeQueryStr + ")";
459  }
460  if (!excludeQueryStr.isEmpty()) {
461  excludeQueryStr = "(" + excludeQueryStr + ")";
462  }
463  if (includeQueryStr.isEmpty() || excludeQueryStr.isEmpty()) {
464  return includeQueryStr + excludeQueryStr;
465  } else {
466  return includeQueryStr + " AND " + excludeQueryStr;
467  }
468  }
469 
470  @NbBundle.Messages({
471  "# {0} - filters",
472  "SearchFiltering.ParentFilter.desc=Paths matching: {0}",
473  "SearchFiltering.ParentFilter.or=, ",
474  "SearchFiltering.ParentFilter.exact=(exact match)",
475  "SearchFiltering.ParentFilter.substring=(substring)",
476  "SearchFiltering.ParentFilter.included=(included)",
477  "SearchFiltering.ParentFilter.excluded=(excluded)"})
478  @Override
479  public String getDesc() {
480  String desc = ""; // NON-NLS
481  for (ParentSearchTerm searchTerm : parentSearchTerms) {
482  if (!desc.isEmpty()) {
483  desc += Bundle.SearchFiltering_ParentFilter_or();
484  }
485  if (searchTerm.isFullPath()) {
486  desc += searchTerm.getSearchStr() + Bundle.SearchFiltering_ParentFilter_exact();
487  } else {
488  desc += searchTerm.getSearchStr() + Bundle.SearchFiltering_ParentFilter_substring();
489  }
490  if (searchTerm.isIncluded()) {
491  desc += Bundle.SearchFiltering_ParentFilter_included();
492  } else {
493  desc += Bundle.SearchFiltering_ParentFilter_excluded();
494  }
495  }
496  desc = Bundle.SearchFiltering_ParentFilter_desc(desc);
497  return desc;
498  }
499  }
500 
504  public static class DataSourceFilter extends AbstractFilter {
505 
506  private final List<DataSource> dataSources;
507 
513  public DataSourceFilter(List<DataSource> dataSources) {
514  this.dataSources = dataSources;
515  }
516 
517  @Override
518  public String getWhereClause() {
519  String queryStr = ""; // NON-NLS
520  for (DataSource ds : dataSources) {
521  if (!queryStr.isEmpty()) {
522  queryStr += ","; // NON-NLS
523  }
524  queryStr += "\'" + ds.getId() + "\'"; // NON-NLS
525  }
526  queryStr = "data_source_obj_id IN (" + queryStr + ")"; // NON-NLS
527  return queryStr;
528  }
529 
530  @NbBundle.Messages({
531  "# {0} - filters",
532  "SearchFiltering.DataSourceFilter.desc=Data source(s): {0}",
533  "SearchFiltering.DataSourceFilter.or=, ",
534  "# {0} - Data source name",
535  "# {1} - Data source ID",
536  "SearchFiltering.DataSourceFilter.datasource={0}({1})",})
537  @Override
538  public String getDesc() {
539  String desc = ""; // NON-NLS
540  for (DataSource ds : dataSources) {
541  if (!desc.isEmpty()) {
542  desc += Bundle.SearchFiltering_DataSourceFilter_or();
543  }
544  desc += Bundle.SearchFiltering_DataSourceFilter_datasource(ds.getName(), ds.getId());
545  }
546  desc = Bundle.SearchFiltering_DataSourceFilter_desc(desc);
547  return desc;
548  }
549  }
550 
555  public static class KeywordListFilter extends AbstractFilter {
556 
557  private final List<String> listNames;
558 
564  public KeywordListFilter(List<String> listNames) {
565  this.listNames = listNames;
566  }
567 
568  @Override
569  public String getWhereClause() {
570  String keywordListPart = concatenateNamesForSQL(listNames);
571 
572  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
573  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = 9 AND attribute_type_ID = 37 "
574  + "AND (" + keywordListPart + "))))"; // NON-NLS
575 
576  return queryStr;
577  }
578 
579  @NbBundle.Messages({
580  "# {0} - filters",
581  "SearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}",})
582  @Override
583  public String getDesc() {
584  return Bundle.SearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames));
585  }
586  }
587 
591  public static class FileTypeFilter extends AbstractFilter {
592 
593  private final List<Type> categories;
594 
600  public FileTypeFilter(List<Type> categories) {
601  this.categories = categories;
602  }
603 
609  public FileTypeFilter(Type category) {
610  this.categories = new ArrayList<>();
611  this.categories.add(category);
612  }
613 
614  @Override
615  public String getWhereClause() {
616  String queryStr = ""; // NON-NLS
617  for (Type cat : categories) {
618  for (String type : cat.getMediaTypes()) {
619  if (!queryStr.isEmpty()) {
620  queryStr += ","; // NON-NLS
621  }
622  queryStr += "\'" + type + "\'"; // NON-NLS
623  }
624  }
625  queryStr = "mime_type IN (" + queryStr + ")"; // NON-NLS
626  return queryStr;
627  }
628 
629  @NbBundle.Messages({
630  "# {0} - filters",
631  "SearchFiltering.FileTypeFilter.desc=Type: {0}",
632  "SearchFiltering.FileTypeFilter.or=, ",})
633  @Override
634  public String getDesc() {
635  String desc = "";
636  for (Type cat : categories) {
637  if (!desc.isEmpty()) {
638  desc += Bundle.SearchFiltering_FileTypeFilter_or();
639  }
640  desc += cat.toString();
641  }
642  desc = Bundle.SearchFiltering_FileTypeFilter_desc(desc);
643  return desc;
644  }
645  }
646 
650  public static class FrequencyFilter extends AbstractFilter {
651 
652  private final List<Frequency> frequencies;
653 
659  public FrequencyFilter(List<Frequency> frequencies) {
660  this.frequencies = frequencies;
661  }
662 
663  @Override
664  public String getWhereClause() {
665  // Since this relies on the central repository database, there is no
666  // query on the case database.
667  return ""; // NON-NLS
668  }
669 
670  @Override
671  public boolean useAlternateFilter() {
672  return true;
673  }
674 
675  @Override
676  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
677  CentralRepository centralRepoDb) throws DiscoveryException {
678  // Set the frequency for each file
680  freqAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb);
681 
682  // If the frequency matches the filter, add the file to the results
683  List<Result> frequencyResults = new ArrayList<>();
684  for (Result file : currentResults) {
685  if (frequencies.contains(file.getFrequency())) {
686  frequencyResults.add(file);
687  }
688  }
689  return frequencyResults;
690  }
691 
692  @NbBundle.Messages({
693  "# {0} - filters",
694  "SearchFiltering.FrequencyFilter.desc=Past occurrences: {0}",
695  "SearchFiltering.FrequencyFilter.or=, ",})
696  @Override
697  public String getDesc() {
698  String desc = ""; // NON-NLS
699  for (Frequency freq : frequencies) {
700  if (!desc.isEmpty()) {
701  desc += Bundle.SearchFiltering_FrequencyFilter_or();
702  }
703  desc += freq.toString();
704  }
705  return Bundle.SearchFiltering_FrequencyFilter_desc(desc);
706  }
707  }
708 
712  public static class KnownAccountTypeFilter extends AbstractFilter {
713 
714  @Override
715  public String getWhereClause() {
716  throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
717  }
718 
719  @Override
720  public boolean useAlternateFilter() {
721  return true;
722  }
723 
724  @Override
725  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
726  CentralRepository centralRepoDb) throws DiscoveryException {
727  List<Result> filteredResults = new ArrayList<>();
728  for (Result result : currentResults) {
729  if (result instanceof ResultDomain) {
730  ResultDomain domain = (ResultDomain) result;
731  if (domain.hasKnownAccountType()) {
732  filteredResults.add(domain);
733  }
734  } else {
735  filteredResults.add(result);
736  }
737  }
738  return filteredResults;
739  }
740 
741  @NbBundle.Messages({
742  "SearchFiltering.KnownAccountTypeFilter.desc=Only domains with known account type"
743  })
744  @Override
745  public String getDesc() {
746  return Bundle.SearchFiltering_KnownAccountTypeFilter_desc();
747  }
748 
749  }
750 
754  public static class PreviouslyNotableFilter extends AbstractFilter {
755 
756  @Override
757  public String getWhereClause() {
758  throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
759  }
760 
761  @Override
762  public boolean useAlternateFilter() {
763  return true;
764  }
765 
766  @Override
767  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
768  CentralRepository centralRepoDb) throws DiscoveryException {
770  previouslyNotableAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb);
771  List<Result> filteredResults = new ArrayList<>();
772  for (Result file : currentResults) {
773  if (file.getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
774  filteredResults.add(file);
775  }
776  }
777  return filteredResults;
778  }
779 
780  @NbBundle.Messages({
781  "SearchFiltering.PreviouslyNotableFilter.desc=Previously marked as notable in central repository"
782  })
783  @Override
784  public String getDesc() {
785  return Bundle.SearchFiltering_PreviouslyNotableFilter_desc();
786  }
787 
788  }
789 
794  public static class HashSetFilter extends AbstractFilter {
795 
796  private final List<String> setNames;
797 
803  public HashSetFilter(List<String> setNames) {
804  this.setNames = setNames;
805  }
806 
807  @Override
808  public String getWhereClause() {
809  String hashSetPart = concatenateNamesForSQL(setNames);
810 
811  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
812  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
813  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
814  + "AND (" + hashSetPart + "))))"; // NON-NLS
815 
816  return queryStr;
817  }
818 
819  @NbBundle.Messages({
820  "# {0} - filters",
821  "FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}",})
822  @Override
823  public String getDesc() {
824  return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames));
825  }
826  }
827 
832  public static class InterestingFileSetFilter extends AbstractFilter {
833 
834  private final List<String> setNames;
835 
841  public InterestingFileSetFilter(List<String> setNames) {
842  this.setNames = setNames;
843  }
844 
845  @Override
846  public String getWhereClause() {
847  String intItemSetPart = concatenateNamesForSQL(setNames);
848 
849  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
850  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
851  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
852  + "AND (" + intItemSetPart + "))))"; // NON-NLS
853 
854  return queryStr;
855  }
856 
857  @NbBundle.Messages({
858  "# {0} - filters",
859  "SearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",})
860  @Override
861  public String getDesc() {
862  return Bundle.SearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames));
863  }
864  }
865 
870  public static class ObjectDetectionFilter extends AbstractFilter {
871 
872  private final List<String> typeNames;
873 
879  public ObjectDetectionFilter(List<String> typeNames) {
880  this.typeNames = typeNames;
881  }
882 
883  @Override
884  public String getWhereClause() {
885  String objTypePart = concatenateNamesForSQL(typeNames);
886 
887  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
888  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
889  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " "
890  + "AND (" + objTypePart + "))))"; // NON-NLS
891 
892  return queryStr;
893  }
894 
895  @NbBundle.Messages({
896  "# {0} - filters",
897  "SearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",})
898  @Override
899  public String getDesc() {
900  return Bundle.SearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames));
901  }
902  }
903 
908  public static class ScoreFilter extends AbstractFilter {
909 
910  private final List<Score> scores;
911 
917  public ScoreFilter(List<Score> scores) {
918  this.scores = scores;
919  }
920 
921  @Override
922  public String getWhereClause() {
923 
924  // Current algorithm:
925  // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag.
926  // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag.
927  String hashsetQueryPart = "";
928  String tagQueryPart = "";
929  String intItemQueryPart = "";
930 
931  if (scores.contains(Score.NOTABLE)) {
932  // do hashset
933  hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") ";
934  }
935 
936  if (scores.contains(Score.INTERESTING)) {
937  // Matches interesting item artifact
938  intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = "
939  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) ";
940  }
941 
942  if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) {
943  // Any tag will work
944  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))";
945  } else if (scores.contains(Score.NOTABLE)) {
946  // Notable tags
947  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = "
948  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
949  } else if (scores.contains(Score.INTERESTING)) {
950  // Non-notable tags
951  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != "
952  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
953  }
954 
955  String queryStr = hashsetQueryPart;
956  if (!intItemQueryPart.isEmpty()) {
957  if (!queryStr.isEmpty()) {
958  queryStr += " OR ";
959  }
960  queryStr += intItemQueryPart;
961  }
962  if (!tagQueryPart.isEmpty()) {
963  if (!queryStr.isEmpty()) {
964  queryStr += " OR ";
965  }
966  queryStr += tagQueryPart;
967  }
968  return queryStr;
969  }
970 
971  @NbBundle.Messages({
972  "# {0} - filters",
973  "SearchFiltering.ScoreFilter.desc=Score(s) of : {0}",})
974  @Override
975  public String getDesc() {
976  return Bundle.SearchFiltering_ScoreFilter_desc(
977  concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList())));
978  }
979  }
980 
985  public static class TagsFilter extends AbstractFilter {
986 
987  private final List<TagName> tagNames;
988 
994  public TagsFilter(List<TagName> tagNames) {
995  this.tagNames = tagNames;
996  }
997 
998  @Override
999  public String getWhereClause() {
1000  String tagIDs = ""; // NON-NLS
1001  for (TagName tagName : tagNames) {
1002  if (!tagIDs.isEmpty()) {
1003  tagIDs += ",";
1004  }
1005  tagIDs += tagName.getId();
1006  }
1007 
1008  String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))";
1009 
1010  return queryStr;
1011  }
1012 
1013  @NbBundle.Messages({
1014  "# {0} - tag names",
1015  "FileSearchFiltering.TagsFilter.desc=Tagged {0}",
1016  "FileSearchFiltering.TagsFilter.or=, ",})
1017  @Override
1018  public String getDesc() {
1019  String desc = ""; // NON-NLS
1020  for (TagName name : tagNames) {
1021  if (!desc.isEmpty()) {
1022  desc += Bundle.FileSearchFiltering_TagsFilter_or();
1023  }
1024  desc += name.getDisplayName();
1025  }
1026  return Bundle.FileSearchFiltering_TagsFilter_desc(desc);
1027  }
1028  }
1029 
1034  public static class UserCreatedFilter extends AbstractFilter {
1035 
1036  @Override
1037  public String getWhereClause() {
1038  return "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
1039  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = "
1040  + BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID() + ")))";
1041  }
1042 
1043  @NbBundle.Messages({
1044  "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",})
1045  @Override
1046  public String getDesc() {
1047  return Bundle.FileSearchFiltering_UserCreatedFilter_desc();
1048  }
1049  }
1050 
1055  public static class NotableFilter extends AbstractFilter {
1056 
1057  @Override
1058  public String getWhereClause() {
1059  // Since this relies on the central repository database, there is no
1060  // query on the case database.
1061  return ""; // NON-NLS
1062  }
1063 
1064  @Override
1065  public boolean useAlternateFilter() {
1066  return true;
1067  }
1068 
1069  @Override
1070  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
1071  CentralRepository centralRepoDb) throws DiscoveryException {
1072 
1073  if (centralRepoDb == null) {
1074  throw new DiscoveryException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS
1075  }
1076 
1077  // We have to have run some kind of SQL filter before getting to this point,
1078  // and should have checked afterward to see if the results were empty.
1079  if (currentResults.isEmpty()) {
1080  throw new DiscoveryException("Can not run on empty list"); // NON-NLS
1081  }
1082 
1083  // The matching files
1084  List<Result> notableResults = new ArrayList<>();
1085 
1086  try {
1088 
1089  for (Result result : currentResults) {
1090  ResultFile file = (ResultFile) result;
1091  if (result.getType() == SearchData.Type.DOMAIN) {
1092  break;
1093  }
1094  if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) {
1095  // Check if this file hash is marked as notable in the CR
1096  String value = file.getFirstInstance().getMd5Hash();
1097  if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) {
1098  notableResults.add(result);
1099  }
1100  }
1101  }
1102  return notableResults;
1104  throw new DiscoveryException("Error querying central repository", ex); // NON-NLS
1105  }
1106  }
1107 
1108  @NbBundle.Messages({
1109  "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",})
1110  @Override
1111  public String getDesc() {
1112  return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc();
1113  }
1114  }
1115 
1119  public static class KnownFilter extends AbstractFilter {
1120 
1121  @Override
1122  public String getWhereClause() {
1123  return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS
1124  }
1125 
1126  @NbBundle.Messages({
1127  "FileSearchFiltering.KnownFilter.desc=which are not known"})
1128  @Override
1129  public String getDesc() {
1130  return Bundle.FileSearchFiltering_KnownFilter_desc();
1131  }
1132  }
1133 
1141  @NbBundle.Messages({
1142  "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",})
1143  private static String concatenateSetNamesForDisplay(List<String> setNames) {
1144  String desc = ""; // NON-NLS
1145  for (String setName : setNames) {
1146  if (!desc.isEmpty()) {
1147  desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma();
1148  }
1149  desc += setName;
1150  }
1151  return desc;
1152  }
1153 
1162  private static String concatenateNamesForSQL(List<String> setNames) {
1163  String result = ""; // NON-NLS
1164  for (String setName : setNames) {
1165  if (!result.isEmpty()) {
1166  result += " OR "; // NON-NLS
1167  }
1168  result += "value_text = \'" + setName + "\'"; // NON-NLS
1169  }
1170  return result;
1171  }
1172 
1176  private SearchFiltering() {
1177  // Class should not be instantiated
1178  }
1179 }
static String concatenateNamesForSQL(List< String > setNames)
ParentSearchTerm(String searchStr, boolean isFullPath, boolean isIncluded)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb)
Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value)
static String concatenateSetNamesForDisplay(List< String > setNames)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb)
List< Result > applyAlternateFilter(List< Result > currentResults, SleuthkitCase caseDb, CentralRepository centralRepoDb)
static List< Result > getResultList(List< AbstractFilter > filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb)

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