Autopsy  4.17.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 
772  List<Result> filteredResults = new ArrayList<>();
773  for (Result file : currentResults) {
774  if (file.getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
775  filteredResults.add(file);
776  }
777  }
778  return filteredResults;
779  }
780 
781  @NbBundle.Messages({
782  "SearchFiltering.PreviouslyNotableFilter.desc=Previously marked as notable in central repository"
783  })
784  @Override
785  public String getDesc() {
786  return Bundle.SearchFiltering_PreviouslyNotableFilter_desc();
787  }
788 
789  }
790 
795  public static class HashSetFilter extends AbstractFilter {
796 
797  private final List<String> setNames;
798 
804  public HashSetFilter(List<String> setNames) {
805  this.setNames = setNames;
806  }
807 
808  @Override
809  public String getWhereClause() {
810  String hashSetPart = concatenateNamesForSQL(setNames);
811 
812  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
813  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
814  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
815  + "AND (" + hashSetPart + "))))"; // NON-NLS
816 
817  return queryStr;
818  }
819 
820  @NbBundle.Messages({
821  "# {0} - filters",
822  "FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}",})
823  @Override
824  public String getDesc() {
825  return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames));
826  }
827  }
828 
833  public static class InterestingFileSetFilter extends AbstractFilter {
834 
835  private final List<String> setNames;
836 
842  public InterestingFileSetFilter(List<String> setNames) {
843  this.setNames = setNames;
844  }
845 
846  @Override
847  public String getWhereClause() {
848  String intItemSetPart = concatenateNamesForSQL(setNames);
849 
850  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
851  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
852  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
853  + "AND (" + intItemSetPart + "))))"; // NON-NLS
854 
855  return queryStr;
856  }
857 
858  @NbBundle.Messages({
859  "# {0} - filters",
860  "SearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",})
861  @Override
862  public String getDesc() {
863  return Bundle.SearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames));
864  }
865  }
866 
871  public static class ObjectDetectionFilter extends AbstractFilter {
872 
873  private final List<String> typeNames;
874 
880  public ObjectDetectionFilter(List<String> typeNames) {
881  this.typeNames = typeNames;
882  }
883 
884  @Override
885  public String getWhereClause() {
886  String objTypePart = concatenateNamesForSQL(typeNames);
887 
888  String queryStr = "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
889  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
890  + " AND attribute_type_ID = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID() + " "
891  + "AND (" + objTypePart + "))))"; // NON-NLS
892 
893  return queryStr;
894  }
895 
896  @NbBundle.Messages({
897  "# {0} - filters",
898  "SearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",})
899  @Override
900  public String getDesc() {
901  return Bundle.SearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames));
902  }
903  }
904 
909  public static class ScoreFilter extends AbstractFilter {
910 
911  private final List<Score> scores;
912 
918  public ScoreFilter(List<Score> scores) {
919  this.scores = scores;
920  }
921 
922  @Override
923  public String getWhereClause() {
924 
925  // Current algorithm:
926  // "Notable" if the file is a match for a notable hashset or has been tagged with a notable tag.
927  // "Interesting" if the file has an interesting item match or has been tagged with a non-notable tag.
928  String hashsetQueryPart = "";
929  String tagQueryPart = "";
930  String intItemQueryPart = "";
931 
932  if (scores.contains(Score.NOTABLE)) {
933  // do hashset
934  hashsetQueryPart = " (known = " + TskData.FileKnown.BAD.getFileKnownValue() + ") ";
935  }
936 
937  if (scores.contains(Score.INTERESTING)) {
938  // Matches interesting item artifact
939  intItemQueryPart = " (obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_type_id = "
940  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ")) ";
941  }
942 
943  if (scores.contains(Score.NOTABLE) && scores.contains(Score.INTERESTING)) {
944  // Any tag will work
945  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags))";
946  } else if (scores.contains(Score.NOTABLE)) {
947  // Notable tags
948  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus = "
949  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
950  } else if (scores.contains(Score.INTERESTING)) {
951  // Non-notable tags
952  tagQueryPart = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE knownStatus != "
953  + TskData.FileKnown.BAD.getFileKnownValue() + ")))";
954  }
955 
956  String queryStr = hashsetQueryPart;
957  if (!intItemQueryPart.isEmpty()) {
958  if (!queryStr.isEmpty()) {
959  queryStr += " OR ";
960  }
961  queryStr += intItemQueryPart;
962  }
963  if (!tagQueryPart.isEmpty()) {
964  if (!queryStr.isEmpty()) {
965  queryStr += " OR ";
966  }
967  queryStr += tagQueryPart;
968  }
969  return queryStr;
970  }
971 
972  @NbBundle.Messages({
973  "# {0} - filters",
974  "SearchFiltering.ScoreFilter.desc=Score(s) of : {0}",})
975  @Override
976  public String getDesc() {
977  return Bundle.SearchFiltering_ScoreFilter_desc(
978  concatenateSetNamesForDisplay(scores.stream().map(p -> p.toString()).collect(Collectors.toList())));
979  }
980  }
981 
986  public static class TagsFilter extends AbstractFilter {
987 
988  private final List<TagName> tagNames;
989 
995  public TagsFilter(List<TagName> tagNames) {
996  this.tagNames = tagNames;
997  }
998 
999  @Override
1000  public String getWhereClause() {
1001  String tagIDs = ""; // NON-NLS
1002  for (TagName tagName : tagNames) {
1003  if (!tagIDs.isEmpty()) {
1004  tagIDs += ",";
1005  }
1006  tagIDs += tagName.getId();
1007  }
1008 
1009  String queryStr = "(obj_id IN (SELECT obj_id FROM content_tags WHERE tag_name_id IN (" + tagIDs + ")))";
1010 
1011  return queryStr;
1012  }
1013 
1014  @NbBundle.Messages({
1015  "# {0} - tag names",
1016  "FileSearchFiltering.TagsFilter.desc=Tagged {0}",
1017  "FileSearchFiltering.TagsFilter.or=, ",})
1018  @Override
1019  public String getDesc() {
1020  String desc = ""; // NON-NLS
1021  for (TagName name : tagNames) {
1022  if (!desc.isEmpty()) {
1023  desc += Bundle.FileSearchFiltering_TagsFilter_or();
1024  }
1025  desc += name.getDisplayName();
1026  }
1027  return Bundle.FileSearchFiltering_TagsFilter_desc(desc);
1028  }
1029  }
1030 
1035  public static class UserCreatedFilter extends AbstractFilter {
1036 
1037  @Override
1038  public String getWhereClause() {
1039  return "(obj_id IN (SELECT obj_id from blackboard_artifacts WHERE artifact_id IN "
1040  + "(SELECT artifact_id FROM blackboard_attributes WHERE artifact_type_id = "
1041  + BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID() + ")))";
1042  }
1043 
1044  @NbBundle.Messages({
1045  "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",})
1046  @Override
1047  public String getDesc() {
1048  return Bundle.FileSearchFiltering_UserCreatedFilter_desc();
1049  }
1050  }
1051 
1056  public static class NotableFilter extends AbstractFilter {
1057 
1058  @Override
1059  public String getWhereClause() {
1060  // Since this relies on the central repository database, there is no
1061  // query on the case database.
1062  return ""; // NON-NLS
1063  }
1064 
1065  @Override
1066  public boolean useAlternateFilter() {
1067  return true;
1068  }
1069 
1070  @Override
1071  public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
1072  CentralRepository centralRepoDb) throws DiscoveryException {
1073 
1074  if (centralRepoDb == null) {
1075  throw new DiscoveryException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS
1076  }
1077 
1078  // We have to have run some kind of SQL filter before getting to this point,
1079  // and should have checked afterward to see if the results were empty.
1080  if (currentResults.isEmpty()) {
1081  throw new DiscoveryException("Can not run on empty list"); // NON-NLS
1082  }
1083 
1084  // The matching files
1085  List<Result> notableResults = new ArrayList<>();
1086 
1087  try {
1089 
1090  for (Result result : currentResults) {
1091  ResultFile file = (ResultFile) result;
1092  if (result.getType() == SearchData.Type.DOMAIN) {
1093  break;
1094  }
1095  if (file.getFirstInstance().getMd5Hash() != null && !file.getFirstInstance().getMd5Hash().isEmpty()) {
1096  // Check if this file hash is marked as notable in the CR
1097  String value = file.getFirstInstance().getMd5Hash();
1098  if (centralRepoDb.getCountArtifactInstancesKnownBad(type, value) > 0) {
1099  notableResults.add(result);
1100  }
1101  }
1102  }
1103  return notableResults;
1105  throw new DiscoveryException("Error querying central repository", ex); // NON-NLS
1106  }
1107  }
1108 
1109  @NbBundle.Messages({
1110  "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",})
1111  @Override
1112  public String getDesc() {
1113  return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc();
1114  }
1115  }
1116 
1120  public static class KnownFilter extends AbstractFilter {
1121 
1122  @Override
1123  public String getWhereClause() {
1124  return "known!=" + TskData.FileKnown.KNOWN.getFileKnownValue(); // NON-NLS
1125  }
1126 
1127  @NbBundle.Messages({
1128  "FileSearchFiltering.KnownFilter.desc=which are not known"})
1129  @Override
1130  public String getDesc() {
1131  return Bundle.FileSearchFiltering_KnownFilter_desc();
1132  }
1133  }
1134 
1142  @NbBundle.Messages({
1143  "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",})
1144  private static String concatenateSetNamesForDisplay(List<String> setNames) {
1145  String desc = ""; // NON-NLS
1146  for (String setName : setNames) {
1147  if (!desc.isEmpty()) {
1148  desc += Bundle.FileSearchFiltering_concatenateSetNamesForDisplay_comma();
1149  }
1150  desc += setName;
1151  }
1152  return desc;
1153  }
1154 
1163  private static String concatenateNamesForSQL(List<String> setNames) {
1164  String result = ""; // NON-NLS
1165  for (String setName : setNames) {
1166  if (!result.isEmpty()) {
1167  result += " OR "; // NON-NLS
1168  }
1169  result += "value_text = \'" + setName + "\'"; // NON-NLS
1170  }
1171  return result;
1172  }
1173 
1177  private SearchFiltering() {
1178  // Class should not be instantiated
1179  }
1180 }
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: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.