Autopsy  4.19.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DiscoveryAttributes.java
Go to the documentation of this file.
1 /*
2  * Autopsy
3  *
4  * Copyright 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.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.logging.Level;
32 import org.openide.util.NbBundle;
40 import org.sleuthkit.datamodel.BlackboardArtifact;
41 import org.sleuthkit.datamodel.BlackboardAttribute;
42 import org.sleuthkit.datamodel.CaseDbAccessManager;
43 import org.sleuthkit.datamodel.ContentTag;
44 import org.sleuthkit.datamodel.SleuthkitCase;
45 import org.sleuthkit.datamodel.TskCoreException;
46 import org.sleuthkit.datamodel.TskData;
47 import java.util.StringJoiner;
49 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CATEGORIZATION;
50 
55 public class DiscoveryAttributes {
56 
57  private final static Logger logger = Logger.getLogger(DiscoveryAttributes.class.getName());
58 
62  public abstract static class AttributeType {
63 
72  public abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result result);
73 
84  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
85  // Default is to do nothing
86  }
87  }
88 
92  public static class FileSizeAttribute extends AttributeType {
93 
94  @Override
96  return new DiscoveryKeyUtils.FileSizeGroupKey(result);
97  }
98  }
99 
103  public static class ParentPathAttribute extends AttributeType {
104 
105  @Override
108  }
109  }
110 
114  static class NoGroupingAttribute extends AttributeType {
115 
116  @Override
117  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
119  }
120  }
121 
125  static class DataSourceAttribute extends AttributeType {
126 
127  @Override
128  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
129  return new DiscoveryKeyUtils.DataSourceGroupKey(result);
130  }
131  }
132 
136  static class FileTypeAttribute extends AttributeType {
137 
138  @Override
139  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
140  return new DiscoveryKeyUtils.FileTypeGroupKey(file);
141  }
142  }
143 
148  static class DomainCategoryAttribute extends AttributeType {
149 
150  @Override
151  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
152  return new DiscoveryKeyUtils.DomainCategoryGroupKey(result);
153  }
154 
155  @Override
156  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
157  CentralRepository centralRepoDb) throws DiscoveryException {
158  try {
159  Map<String, Set<String>> domainsToCategories = getDomainsWithWebCategories(caseDb);
160  for (Result result : results) {
161  if (result instanceof ResultDomain) {
162  ResultDomain domain = (ResultDomain) result;
163  domain.addWebCategories(domainsToCategories.get(domain.getDomain()));
164  }
165  }
166  } catch (TskCoreException | InterruptedException ex) {
167  throw new DiscoveryException("Error fetching TSK_WEB_CATEGORY artifacts from the database", ex);
168  }
169  }
170 
176  private Map<String, Set<String>> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
177  Map<String, Set<String>> domainToCategory = new HashMap<>();
178 
179  for (BlackboardArtifact artifact : caseDb.getBlackboardArtifacts(TSK_WEB_CATEGORIZATION)) {
180  if (Thread.currentThread().isInterrupted()) {
181  throw new InterruptedException();
182  }
183  BlackboardAttribute webCategory = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
184  BlackboardAttribute domain = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
185  if (webCategory != null && domain != null) {
186  String domainDisplayName = domain.getValueString().trim().toLowerCase();
187  if (!domainToCategory.containsKey(domainDisplayName)) {
188  domainToCategory.put(domainDisplayName, new HashSet<>());
189  }
190  domainToCategory.get(domainDisplayName).add(webCategory.getValueString());
191  }
192  }
193  return domainToCategory;
194  }
195  }
196 
200  static class KeywordListAttribute extends AttributeType {
201 
202  @Override
203  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
204  return new DiscoveryKeyUtils.KeywordListGroupKey((ResultFile) file);
205  }
206 
207  @Override
208  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
209  CentralRepository centralRepoDb) throws DiscoveryException {
210 
211  // Get pairs of (object ID, keyword list name) for all files in the list of files that have
212  // keyword list hits.
213  String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
214  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
215 
216  SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(results);
217  try {
218  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
219  } catch (TskCoreException ex) {
220  throw new DiscoveryException("Error looking up keyword list attributes", ex); // NON-NLS
221  }
222  }
223 
229  private static class SetKeywordListNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
230 
231  List<Result> resultFiles;
232 
238  SetKeywordListNamesCallback(List<Result> resultFiles) {
239  this.resultFiles = resultFiles;
240  }
241 
242  @Override
243  public void process(ResultSet rs) {
244  try {
245  // Create a temporary map of object ID to ResultFile
246  Map<Long, ResultFile> tempMap = new HashMap<>();
247  for (Result result : resultFiles) {
248  if (result.getType() == SearchData.Type.DOMAIN) {
249  break;
250  }
251  ResultFile file = (ResultFile) result;
252  tempMap.put(file.getFirstInstance().getId(), file);
253  }
254 
255  while (rs.next()) {
256  try {
257  Long objId = rs.getLong("object_id"); // NON-NLS
258  String keywordListName = rs.getString("set_name"); // NON-NLS
259 
260  tempMap.get(objId).addKeywordListName(keywordListName);
261 
262  } catch (SQLException ex) {
263  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
264  }
265  }
266  } catch (SQLException ex) {
267  logger.log(Level.SEVERE, "Failed to get keyword list names", ex); // NON-NLS
268  }
269  }
270  }
271  }
272 
282  private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) {
283  final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
284  for (ResultDomain domainInstance : domainsBatch) {
285  try {
286  final String domainValue = domainInstance.getDomain();
287  final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
288  final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
289  bucket.add(domainInstance);
290  resultDomainTable.put(normalizedDomain, bucket);
292  logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
293  }
294  }
295  return resultDomainTable;
296  }
297 
303  private static String createCSV(Set<String> values) {
304  StringJoiner joiner = new StringJoiner(", ");
305  for (String value : values) {
306  joiner.add("'" + value + "'");
307  }
308  return joiner.toString();
309  }
310 
314  static class PreviouslyNotableAttribute extends AttributeType {
315 
316  static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
317 
318  @Override
319  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
320  return new DiscoveryKeyUtils.PreviouslyNotableGroupKey(result);
321  }
322 
323  @Override
324  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
325  CentralRepository centralRepoDb) throws DiscoveryException {
326 
327  if (centralRepoDb != null) {
328  processFilesWithCr(results, centralRepoDb);
329  }
330  }
331 
332  private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException {
333 
334  List<ResultDomain> domainsBatch = new ArrayList<>();
335  for (Result result : results) {
336  if (result.getType() == SearchData.Type.DOMAIN) {
337  domainsBatch.add((ResultDomain) result);
338  if (domainsBatch.size() == DOMAIN_BATCH_SIZE) {
339  queryPreviouslyNotable(domainsBatch, centralRepo);
340  domainsBatch.clear();
341  }
342  }
343  }
344 
345  queryPreviouslyNotable(domainsBatch, centralRepo);
346  }
347 
348  private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException {
349  if (domainsBatch.isEmpty()) {
350  return;
351  }
352 
353  try {
354  final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
355  final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType);
356  final String values = createCSV(resultDomainTable.keySet());
357 
358  final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
359  final String domainFrequencyQuery = " value AS domain_name "
360  + "FROM " + tableName + " "
361  + "WHERE value IN (" + values + ") "
362  + "AND known_status = " + TskData.FileKnown.BAD.getFileKnownValue();
363 
364  final DomainPreviouslyNotableCallback previouslyNotableCallback = new DomainPreviouslyNotableCallback(resultDomainTable);
365  centralRepo.processSelectClause(domainFrequencyQuery, previouslyNotableCallback);
366 
367  if (previouslyNotableCallback.getCause() != null) {
368  throw previouslyNotableCallback.getCause();
369  }
370  } catch (CentralRepoException | SQLException ex) {
371  throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
372  }
373  }
374 
375  private static class DomainPreviouslyNotableCallback implements InstanceTableCallback {
376 
377  private final Map<String, List<ResultDomain>> domainLookup;
378  private SQLException sqlCause;
379 
380  private DomainPreviouslyNotableCallback(Map<String, List<ResultDomain>> domainLookup) {
381  this.domainLookup = domainLookup;
382  }
383 
384  @Override
385  public void process(ResultSet resultSet) {
386  try {
387  while (resultSet.next()) {
388  String domain = resultSet.getString("domain_name");
389  List<ResultDomain> domainInstances = domainLookup.get(domain);
390  for (ResultDomain domainInstance : domainInstances) {
391  domainInstance.markAsPreviouslyNotableInCR();
392  }
393  }
394  } catch (SQLException ex) {
395  this.sqlCause = ex;
396  }
397  }
398 
402  SQLException getCause() {
403  return this.sqlCause;
404  }
405  }
406  }
407 
411  static class FrequencyAttribute extends AttributeType {
412 
413  static final int BATCH_SIZE = 50; // Number of hashes to look up at one time
414 
415  static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
416 
417  @Override
418  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
419  return new DiscoveryKeyUtils.FrequencyGroupKey(file);
420  }
421 
422  @Override
423  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
424  CentralRepository centralRepoDb) throws DiscoveryException {
425  if (centralRepoDb == null) {
426  for (Result result : results) {
427  if (result.getFrequency() == SearchData.Frequency.UNKNOWN && result.getKnown() == TskData.FileKnown.KNOWN) {
428  result.setFrequency(SearchData.Frequency.KNOWN);
429  }
430  }
431  } else {
432  processResultFilesForCR(results, centralRepoDb);
433  }
434  }
435 
444  private void processResultFilesForCR(List<Result> results,
445  CentralRepository centralRepoDb) throws DiscoveryException {
446  List<ResultFile> currentFiles = new ArrayList<>();
447  Set<String> hashesToLookUp = new HashSet<>();
448  List<ResultDomain> domainsToQuery = new ArrayList<>();
449  for (Result result : results) {
450  // If frequency was already calculated, skip...
451  if (result.getFrequency() == SearchData.Frequency.UNKNOWN) {
452  if (result.getKnown() == TskData.FileKnown.KNOWN) {
453  result.setFrequency(SearchData.Frequency.KNOWN);
454  }
455 
456  if (result.getType() != SearchData.Type.DOMAIN) {
457  ResultFile file = (ResultFile) result;
458  if (file.getFirstInstance().getMd5Hash() != null
459  && !file.getFirstInstance().getMd5Hash().isEmpty()) {
460  hashesToLookUp.add(file.getFirstInstance().getMd5Hash());
461  currentFiles.add(file);
462  }
463 
464  if (hashesToLookUp.size() >= BATCH_SIZE) {
465  computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
466 
467  hashesToLookUp.clear();
468  currentFiles.clear();
469  }
470  } else {
471  domainsToQuery.add((ResultDomain) result);
472  if (domainsToQuery.size() == DOMAIN_BATCH_SIZE) {
473  queryDomainFrequency(domainsToQuery, centralRepoDb);
474 
475  domainsToQuery.clear();
476  }
477  }
478  }
479  }
480 
481  queryDomainFrequency(domainsToQuery, centralRepoDb);
482  computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
483  }
484  }
485 
494  private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException {
495  if (domainsToQuery.isEmpty()) {
496  return;
497  }
498  try {
499  final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
500  final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType);
501  final String values = createCSV(resultDomainTable.keySet());
502  final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
503  final String domainFrequencyQuery = " value AS domain_name, COUNT(value) AS frequency FROM"
504  + "(SELECT DISTINCT case_id, value FROM "
505  + tableName
506  + " WHERE value IN ("
507  + values
508  + ")) AS foo GROUP BY value";
509 
510  final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
511  centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
512 
513  if (frequencyCallback.getCause() != null) {
514  throw frequencyCallback.getCause();
515  }
516  } catch (CentralRepoException | SQLException ex) {
517  throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
518  }
519  }
520 
524  private static class DomainFrequencyCallback implements InstanceTableCallback {
525 
526  private final Map<String, List<ResultDomain>> domainLookup;
527  private SQLException sqlCause;
528 
534  private DomainFrequencyCallback(Map<String, List<ResultDomain>> domainLookup) {
535  this.domainLookup = domainLookup;
536  }
537 
538  @Override
539  public void process(ResultSet resultSet) {
540  try {
541  while (resultSet.next()) {
542  String domain = resultSet.getString("domain_name");
543  Long frequency = resultSet.getLong("frequency");
544 
545  List<ResultDomain> domainInstances = domainLookup.get(domain);
546  for (ResultDomain domainInstance : domainInstances) {
547  domainInstance.setFrequency(SearchData.Frequency.fromCount(frequency));
548  }
549  }
550  } catch (SQLException ex) {
551  this.sqlCause = ex;
552  }
553  }
554 
560  SQLException getCause() {
561  return this.sqlCause;
562  }
563  }
564 
569  private static class FrequencyCallback implements InstanceTableCallback {
570 
571  private final List<ResultFile> files;
572 
578  private FrequencyCallback(List<ResultFile> files) {
579  this.files = new ArrayList<>(files);
580  }
581 
582  @Override
583  public void process(ResultSet resultSet) {
584  try {
585 
586  while (resultSet.next()) {
587  String hash = resultSet.getString(1);
588  int count = resultSet.getInt(2);
589  for (Iterator<ResultFile> iterator = files.iterator(); iterator.hasNext();) {
590  ResultFile file = iterator.next();
591  if (file.getFirstInstance().getMd5Hash().equalsIgnoreCase(hash)) {
593  iterator.remove();
594  }
595  }
596  }
597 
598  // The files left had no matching entries in the CR, so mark them as unique
599  for (ResultFile file : files) {
600  file.setFrequency(SearchData.Frequency.UNIQUE);
601  }
602  } catch (SQLException ex) {
603  logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
604  }
605  }
606  }
607 
611  static class HashHitsAttribute extends AttributeType {
612 
613  @Override
614  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
615  if (result.getType() == SearchData.Type.DOMAIN) {
616  return null;
617  }
618  return new DiscoveryKeyUtils.HashHitsGroupKey((ResultFile) result);
619  }
620 
621  @Override
622  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
623  CentralRepository centralRepoDb) throws DiscoveryException {
624 
625  // Get pairs of (object ID, hash set name) for all files in the list of files that have
626  // hash set hits.
627  String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
628  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
629 
630  HashSetNamesCallback callback = new HashSetNamesCallback(results);
631  try {
632  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
633  } catch (TskCoreException ex) {
634  throw new DiscoveryException("Error looking up hash set attributes", ex); // NON-NLS
635  }
636  }
637 
642  private static class HashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
643 
644  List<Result> results;
645 
651  HashSetNamesCallback(List<Result> results) {
652  this.results = results;
653  }
654 
655  @Override
656  public void process(ResultSet rs) {
657  try {
658  // Create a temporary map of object ID to ResultFile
659  Map<Long, ResultFile> tempMap = new HashMap<>();
660  for (Result result : results) {
661  if (result.getType() == SearchData.Type.DOMAIN) {
662  return;
663  }
664  ResultFile file = (ResultFile) result;
665  tempMap.put(file.getFirstInstance().getId(), file);
666  }
667 
668  while (rs.next()) {
669  try {
670  Long objId = rs.getLong("object_id"); // NON-NLS
671  String hashSetName = rs.getString("set_name"); // NON-NLS
672 
673  tempMap.get(objId).addHashSetName(hashSetName);
674 
675  } catch (SQLException ex) {
676  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
677  }
678  }
679  } catch (SQLException ex) {
680  logger.log(Level.SEVERE, "Failed to get hash set names", ex); // NON-NLS
681  }
682  }
683  }
684  }
685 
689  static class InterestingItemAttribute extends AttributeType {
690 
691  @Override
692  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
694  }
695 
696  @Override
697  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
698  CentralRepository centralRepoDb) throws DiscoveryException {
699 
700  // Get pairs of (object ID, interesting item set name) for all files in the list of files that have
701  // interesting file set hits.
702  String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
703  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
704 
705  InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(results);
706  try {
707  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
708  } catch (TskCoreException ex) {
709  throw new DiscoveryException("Error looking up interesting file set attributes", ex); // NON-NLS
710  }
711  }
712 
718  private static class InterestingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
719 
720  List<Result> results;
721 
728  InterestingFileSetNamesCallback(List<Result> results) {
729  this.results = results;
730  }
731 
732  @Override
733  public void process(ResultSet rs) {
734  try {
735  // Create a temporary map of object ID to ResultFile
736  Map<Long, ResultFile> tempMap = new HashMap<>();
737  for (Result result : results) {
738  if (result.getType() == SearchData.Type.DOMAIN) {
739  return;
740  }
741  ResultFile file = (ResultFile) result;
742  tempMap.put(file.getFirstInstance().getId(), file);
743  }
744 
745  while (rs.next()) {
746  try {
747  Long objId = rs.getLong("object_id"); // NON-NLS
748  String setName = rs.getString("set_name"); // NON-NLS
749 
750  tempMap.get(objId).addInterestingSetName(setName);
751 
752  } catch (SQLException ex) {
753  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
754  }
755  }
756  } catch (SQLException ex) {
757  logger.log(Level.SEVERE, "Failed to get interesting file set names", ex); // NON-NLS
758  }
759  }
760  }
761  }
762 
766  static class LastActivityDateAttribute extends AttributeType {
767 
768  @Override
769  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
770  return new DiscoveryKeyUtils.LastActivityDateGroupKey(result);
771  }
772 
773  }
774 
778  static class FirstActivityDateAttribute extends AttributeType {
779 
780  @Override
781  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
782  return new DiscoveryKeyUtils.FirstActivityDateGroupKey(result);
783  }
784 
785  }
786 
791  static class PageViewsAttribute extends AttributeType {
792 
793  @Override
794  public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
795  return new DiscoveryKeyUtils.PageViewsGroupKey(result);
796  }
797  }
798 
802  static class ObjectDetectedAttribute extends AttributeType {
803 
804  @Override
805  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
806  return new DiscoveryKeyUtils.ObjectDetectedGroupKey((ResultFile) file);
807  }
808 
809  @Override
810  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
811  CentralRepository centralRepoDb) throws DiscoveryException {
812 
813  // Get pairs of (object ID, object type name) for all files in the list of files that have
814  // objects detected
815  String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(),
816  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
817 
818  ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(results);
819  try {
820  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
821  } catch (TskCoreException ex) {
822  throw new DiscoveryException("Error looking up object detected attributes", ex); // NON-NLS
823  }
824  }
825 
831  private static class ObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
832 
833  List<Result> results;
834 
840  ObjectDetectedNamesCallback(List<Result> results) {
841  this.results = results;
842  }
843 
844  @Override
845  public void process(ResultSet rs) {
846  try {
847  // Create a temporary map of object ID to ResultFile
848  Map<Long, ResultFile> tempMap = new HashMap<>();
849  for (Result result : results) {
850  if (result.getType() == SearchData.Type.DOMAIN) {
851  return;
852  }
853  ResultFile file = (ResultFile) result;
854  tempMap.put(file.getFirstInstance().getId(), file);
855  }
856 
857  while (rs.next()) {
858  try {
859  Long objId = rs.getLong("object_id"); // NON-NLS
860  String setName = rs.getString("set_name"); // NON-NLS
861 
862  tempMap.get(objId).addObjectDetectedName(setName);
863 
864  } catch (SQLException ex) {
865  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
866  }
867  }
868  } catch (SQLException ex) {
869  logger.log(Level.SEVERE, "Failed to get object detected names", ex); // NON-NLS
870  }
871  }
872  }
873  }
874 
878  static class FileTagAttribute extends AttributeType {
879 
880  @Override
881  public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
882  return new DiscoveryKeyUtils.FileTagGroupKey((ResultFile) file);
883  }
884 
885  @Override
886  public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
887  CentralRepository centralRepoDb) throws DiscoveryException {
888 
889  try {
890  for (Result result : results) {
891  if (result.getType() == SearchData.Type.DOMAIN) {
892  return;
893  }
894  ResultFile file = (ResultFile) result;
895  List<ContentTag> contentTags = caseDb.getContentTagsByContent(file.getFirstInstance());
896 
897  for (ContentTag tag : contentTags) {
898  result.addTagName(tag.getName().getDisplayName());
899  }
900  }
901  } catch (TskCoreException ex) {
902  throw new DiscoveryException("Error looking up file tag attributes", ex); // NON-NLS
903  }
904  }
905  }
906 
910  @NbBundle.Messages({
911  "DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type",
912  "DiscoveryAttributes.GroupingAttributeType.frequency.displayName=Past Occurrences",
913  "DiscoveryAttributes.GroupingAttributeType.keywordList.displayName=Keyword",
914  "DiscoveryAttributes.GroupingAttributeType.size.displayName=File Size",
915  "DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source",
916  "DiscoveryAttributes.GroupingAttributeType.parent.displayName=Parent Folder",
917  "DiscoveryAttributes.GroupingAttributeType.hash.displayName=Hash Set",
918  "DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item",
919  "DiscoveryAttributes.GroupingAttributeType.tag.displayName=Tag",
920  "DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected",
921  "DiscoveryAttributes.GroupingAttributeType.lastDate.displayName=Final Activity Date",
922  "DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date",
923  "DiscoveryAttributes.GroupingAttributeType.pageViews.displayName=Page Views",
924  "DiscoveryAttributes.GroupingAttributeType.none.displayName=None",
925  "DiscoveryAttributes.GroupingAttributeType.previouslyNotable.displayName=Previous Notability",
926  "DiscoveryAttributes.GroupingAttributeType.webCategory.displayName=Domain Category"})
927  public enum GroupingAttributeType {
928  FILE_SIZE(new FileSizeAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_size_displayName()),
929  FREQUENCY(new FrequencyAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_frequency_displayName()),
930  KEYWORD_LIST_NAME(new KeywordListAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_keywordList_displayName()),
931  DATA_SOURCE(new DataSourceAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_datasource_displayName()),
932  PARENT_PATH(new ParentPathAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_parent_displayName()),
933  HASH_LIST_NAME(new HashHitsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_hash_displayName()),
934  INTERESTING_ITEM_SET(new InterestingItemAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_interestingItem_displayName()),
935  FILE_TAG(new FileTagAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_tag_displayName()),
936  OBJECT_DETECTED(new ObjectDetectedAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_object_displayName()),
937  LAST_ACTIVITY_DATE(new LastActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_lastDate_displayName()),
938  FIRST_ACTIVITY_DATE(new FirstActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_firstDate_displayName()),
939  PAGE_VIEWS(new PageViewsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_pageViews_displayName()),
940  NO_GROUPING(new NoGroupingAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_none_displayName()),
941  PREVIOUSLY_NOTABLE(new PreviouslyNotableAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_previouslyNotable_displayName()),
942  DOMAIN_CATEGORY(new DomainCategoryAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_webCategory_displayName());
943 
945  private final String displayName;
946 
955  GroupingAttributeType(AttributeType attributeType, String displayName) {
956  this.attributeType = attributeType;
957  this.displayName = displayName;
958  }
959 
960  @Override
961  public String toString() {
962  return displayName;
963  }
964 
971  return attributeType;
972  }
973 
979  public static List<GroupingAttributeType> getOptionsForGroupingForFiles() {
980  return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
981  }
982 
988  public static List<GroupingAttributeType> getOptionsForGroupingForDomains() {
990  return Arrays.asList(PAGE_VIEWS, FREQUENCY, LAST_ACTIVITY_DATE, FIRST_ACTIVITY_DATE, PREVIOUSLY_NOTABLE, DOMAIN_CATEGORY);
991  } else {
992  return Arrays.asList(PAGE_VIEWS, LAST_ACTIVITY_DATE, FIRST_ACTIVITY_DATE, DOMAIN_CATEGORY);
993  }
994  }
995  }
996 
1005  private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb) {
1006 
1007  if (hashesToLookUp.isEmpty()) {
1008  return;
1009  }
1010 
1011  String hashes = String.join("','", hashesToLookUp);
1012  hashes = "'" + hashes + "'";
1013  try {
1015  String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
1016 
1017  String selectClause = " value, COUNT(value) FROM "
1018  + "(SELECT DISTINCT case_id, value FROM " + tableName
1019  + " WHERE value IN ("
1020  + hashes
1021  + ")) AS foo GROUP BY value";
1022 
1023  FrequencyCallback callback = new FrequencyCallback(currentFiles);
1024  centralRepoDb.processSelectClause(selectClause, callback);
1025 
1026  } catch (CentralRepoException ex) {
1027  logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
1028  }
1029 
1030  }
1031 
1045  private static String createSetNameClause(List<Result> results,
1046  int artifactTypeID, int setNameAttrID) throws DiscoveryException {
1047 
1048  // Concatenate the object IDs in the list of files
1049  String objIdList = ""; // NON-NLS
1050  for (Result result : results) {
1051  if (result.getType() == SearchData.Type.DOMAIN) {
1052  break;
1053  }
1054  ResultFile file = (ResultFile) result;
1055  if (!objIdList.isEmpty()) {
1056  objIdList += ","; // NON-NLS
1057  }
1058  objIdList += "\'" + file.getFirstInstance().getId() + "\'"; // NON-NLS
1059  }
1060 
1061  // Get pairs of (object ID, set name) for all files in the list of files that have
1062  // the given artifact type.
1063  return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name "
1064  + "FROM blackboard_artifacts "
1065  + "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id "
1066  + "WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID + "\' "
1067  + "AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID + "\' "
1068  + "AND blackboard_artifacts.obj_id IN (" + objIdList
1069  + ") "; // NON-NLS
1070  }
1071 
1076  // Class should not be instantiated
1077  }
1078 }
static String createSetNameClause(List< Result > results, int artifactTypeID, int setNameAttrID)
abstract TskData.FileKnown getKnown()
static void computeFrequency(Set< String > hashesToLookUp, List< ResultFile > currentFiles, CentralRepository centralRepoDb)
final void setFrequency(SearchData.Frequency frequency)
Definition: Result.java:81
static String correlationTypeToInstanceTableName(CorrelationAttributeInstance.Type type)
static String normalize(CorrelationAttributeInstance.Type attributeType, String data)
void addAttributeToResults(List< Result > results, SleuthkitCase caseDb, CentralRepository centralRepoDb)
abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result result)
void addKeywordListName(String keywordListName)
static Map< String, List< ResultDomain > > organizeByValue(List< ResultDomain > domainsBatch, CorrelationAttributeInstance.Type attributeType)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId)
void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback)
static void queryDomainFrequency(List< ResultDomain > domainsToQuery, CentralRepository centralRepository)

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