Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Artifacts.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2023 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.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Set;
30 import java.util.logging.Level;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.openide.nodes.ChildFactory;
34 import org.openide.nodes.Children;
35 import org.openide.nodes.Node;
36 import org.openide.nodes.Sheet;
37 import org.openide.util.Lookup;
38 import org.openide.util.NbBundle;
39 import org.openide.util.WeakListeners;
40 import org.openide.util.lookup.Lookups;
48 import org.sleuthkit.datamodel.BlackboardArtifact;
49 import org.sleuthkit.datamodel.SleuthkitCase;
50 import org.sleuthkit.datamodel.TskCoreException;
52 import org.sleuthkit.datamodel.BlackboardArtifact.Category;
53 import org.python.google.common.collect.Sets;
54 import org.sleuthkit.datamodel.Blackboard;
55 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
56 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
57 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
58 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
59 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
60 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
61 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
62 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
63 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ITEM;
64 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
65 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
66 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
67 import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_MALWARE;
68 
72 public class Artifacts {
73 
76 
80  static class BaseArtifactNode extends DisplayableItemNode {
81 
90  BaseArtifactNode(Children children, String icon, String name, String displayName) {
91  super(children, Lookups.singleton(name));
92  super.setName(name);
93  super.setDisplayName(displayName);
94  this.setIconBaseWithExtension(icon); //NON-NLS
95  }
96 
97  @Override
98  public boolean isLeafTypeNode() {
99  return false;
100  }
101 
102  @Override
103  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
104  return visitor.visit(this);
105  }
106 
107  @Override
108  protected Sheet createSheet() {
109  Sheet sheet = super.createSheet();
110  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
111  if (sheetSet == null) {
112  sheetSet = Sheet.createPropertiesSet();
113  sheet.put(sheetSet);
114  }
115 
116  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
117  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
118  NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
119  super.getDisplayName()));
120  return sheet;
121  }
122 
123  @Override
124  public String getItemType() {
125  return getClass().getName();
126  }
127  }
128 
132  private static class TypeNodeKey {
133 
135  private final Set<BlackboardArtifact.Type> applicableTypes;
136 
145  TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
146  this(new TypeNode(type, dsObjId), type);
147  }
148 
156  TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
157  this.node = typeNode;
158  this.applicableTypes = Stream.of(types)
159  .filter(t -> t != null)
160  .collect(Collectors.toSet());
161  }
162 
168  UpdatableCountTypeNode getNode() {
169  return node;
170  }
171 
177  Set<BlackboardArtifact.Type> getApplicableTypes() {
178  return applicableTypes;
179  }
180 
181  @Override
182  public int hashCode() {
183  int hash = 3;
184  hash = 61 * hash + Objects.hashCode(this.applicableTypes);
185  return hash;
186  }
187 
188  @Override
189  public boolean equals(Object obj) {
190  if (this == obj) {
191  return true;
192  }
193  if (obj == null) {
194  return false;
195  }
196  if (getClass() != obj.getClass()) {
197  return false;
198  }
199  final TypeNodeKey other = (TypeNodeKey) obj;
200  if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
201  return false;
202  }
203  return true;
204  }
205 
206  }
207 
212  static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
213 
214  private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
215 
219  @SuppressWarnings("deprecation")
220  private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
221  // these are shown in other parts of the UI (and different node types)
222  TSK_DATA_SOURCE_USAGE,
223  TSK_GEN_INFO,
224  new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
225  TSK_TL_EVENT,
226  //This is not meant to be shown in the UI at all. It is more of a meta artifact.
227  TSK_ASSOCIATED_OBJECT
228  );
229 
244  @SuppressWarnings("deprecation")
245  private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
246 
247  int typeId = type.getTypeID();
248  if (TSK_EMAIL_MSG.getTypeID() == typeId) {
249  EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
250  return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
251 
252  } else if (TSK_ACCOUNT.getTypeID() == typeId) {
253  Accounts.AccountsRootNode accountsNode = new Accounts(dsObjId).new AccountsRootNode();
254  return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
255 
256  } else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
257  KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
258  return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
259 
260  } else if (TSK_INTERESTING_ITEM.getTypeID() == typeId) {
261  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ITEM, dsObjId).new RootNode();
262  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ITEM);
263  } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId) {
264  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode();
265  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT);
266  } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
267  InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode();
268  return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT);
269  } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
270  HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
271  return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
272  } else if (TSK_MALWARE.getTypeID() == typeId) {
273  MalwareHits.RootNode malwareHits = new MalwareHits(skCase, dsObjId).new RootNode();
274  return new TypeNodeKey(malwareHits, TSK_MALWARE);
275  } else {
276  return new TypeNodeKey(type, dsObjId);
277  }
278  }
279 
280  // maps the artifact type to its child node
281  private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
282  private final long filteringDSObjId;
283 
289  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
290  private final Category category;
291 
292  private final PropertyChangeListener weakPcl;
293 
302  TypeFactory(Category category, long filteringDSObjId) {
303  super();
304  this.filteringDSObjId = filteringDSObjId;
305  this.category = category;
306 
307  PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
308  String eventType = evt.getPropertyName();
309  if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
310  // case was closed. Remove listeners so that we don't get called with a stale case handle
311  if (evt.getNewValue() == null) {
312  removeNotify();
313  }
314  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
315  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
322  try {
323  Case.getCurrentCaseThrows();
324  refresh(false);
325  } catch (NoCurrentCaseException notUsed) {
329  }
330  }
331  };
332 
333  weakPcl = WeakListeners.propertyChange(pcl, null);
334  }
335 
336  @Override
337  protected void addNotify() {
338  super.addNotify();
339  refreshThrottler.registerForIngestModuleEvents();
340  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
341  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
342  }
343 
344  @Override
345  protected void finalize() throws Throwable {
346  super.finalize();
347  refreshThrottler.unregisterEventListener();
348  IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
349  Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
350  typeNodeMap.clear();
351  }
352 
353  @Override
354  protected boolean createKeys(List<TypeNodeKey> list) {
355  try {
356  // Get all types in use
357  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
358  List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
359  ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
360  : skCase.getArtifactTypesInUse();
361 
362  List<TypeNodeKey> allKeysSorted = types.stream()
363  // filter types by category and ensure they are not in the list of ignored types
364  .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
365  .map(tp -> {
366  // if typeNodeMap already contains key, update the relevant node and return the node
367  if (typeNodeMap.containsKey(tp)) {
368  TypeNodeKey typeKey = typeNodeMap.get(tp);
369  typeKey.getNode().updateDisplayName();
370  return typeKey;
371  } else {
372  // if key is not in map, create the type key and add to map
373  TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
374  for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
375  typeNodeMap.put(recordType, newTypeKey);
376  }
377  return newTypeKey;
378  }
379  })
380  // ensure record is returned
381  .filter(record -> record != null)
382  // there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
383  // ensure the keys are distinct
384  .distinct()
385  // sort by display name
386  .sorted((a, b) -> {
387  String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
388  String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
389  return aSafe.compareToIgnoreCase(bSafe);
390  })
391  .collect(Collectors.toList());
392 
393  list.addAll(allKeysSorted);
394 
395  } catch (NoCurrentCaseException ex) {
396  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
397  } catch (TskCoreException ex) {
398  logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
399  }
400  return true;
401  }
402 
403  @Override
404  protected Node createNodeForKey(TypeNodeKey key) {
405  return key.getNode();
406  }
407 
408  @Override
409  public void refresh() {
410  refresh(false);
411  }
412 
413  @Override
414  public boolean isRefreshRequired(PropertyChangeEvent evt) {
415  String eventType = evt.getPropertyName();
416  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
422  try {
423  Case.getCurrentCaseThrows();
429  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
430  if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
431  && !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
432  return true;
433  }
434  } catch (NoCurrentCaseException notUsed) {
438  }
439  }
440  return false;
441  }
442  }
443 
448  public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
449 
450  private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
451 
452  private final Set<BlackboardArtifact.Type> types;
453  private final long filteringDSObjId;
454  private long childCount = 0;
455  private final String baseName;
456 
469  public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
470  long filteringDSObjId, BlackboardArtifact.Type... types) {
471 
472  super(children, lookup);
473  this.types = Stream.of(types).collect(Collectors.toSet());
474  this.filteringDSObjId = filteringDSObjId;
475  this.baseName = baseName;
476  updateDisplayName();
477  }
478 
484  protected long getChildCount() {
485  return this.childCount;
486  }
487 
497  protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
498  int count = 0;
499  for (BlackboardArtifact.Type type : this.types) {
500  if (filteringDSObjId > 0) {
501  count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
502  } else {
503  count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
504  }
505  }
506  return count;
507  }
508 
513  void updateDisplayName() {
514  try {
515  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
516  this.childCount = fetchChildCount(skCase);
517  } catch (NoCurrentCaseException ex) {
518  logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
519  } catch (TskCoreException ex) {
520  logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
521  }
522  super.setDisplayName(this.baseName + " (" + this.childCount + ")");
523  }
524  }
525 
532  static class TypeNode extends UpdatableCountTypeNode {
533 
534  private final BlackboardArtifact.Type type;
535 
544  TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
545  super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
546  Lookups.singleton(type.getDisplayName()),
547  type.getDisplayName(),
548  filteringDSObjId,
549  type);
550 
551  super.setName(type.getTypeName());
552  this.type = type;
553  String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
554  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
555  }
556 
557  @Override
558  protected Sheet createSheet() {
559  Sheet sheet = super.createSheet();
560  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
561  if (sheetSet == null) {
562  sheetSet = Sheet.createPropertiesSet();
563  sheet.put(sheetSet);
564  }
565 
566  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
567  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
568  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
569  type.getDisplayName()));
570 
571  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
572  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
573  NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
574  getChildCount()));
575 
576  return sheet;
577  }
578 
579  @Override
580  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
581  return visitor.visit(this);
582  }
583 
584  @Override
585  public boolean isLeafTypeNode() {
586  return true;
587  }
588 
589  @Override
590  public String getItemType() {
591  return getClass().getName() + type.getDisplayName();
592  }
593  }
594 
598  private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
599 
600  private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
601  private final BlackboardArtifact.Type type;
602 
608  private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
609  private final long filteringDSObjId;
610 
619  ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
620  super(type.getTypeName());
621  this.type = type;
622  this.filteringDSObjId = filteringDSObjId;
623  }
624 
625  private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
626  String eventType = evt.getPropertyName();
627  if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
628  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
635  try {
637  refresh(false);
638  } catch (NoCurrentCaseException notUsed) {
642  }
643  }
644  };
645 
646  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
647 
648  @Override
649  protected void onAdd() {
650  refreshThrottler.registerForIngestModuleEvents();
652  }
653 
654  @Override
655  protected void onRemove() {
656  if (refreshThrottler != null) {
657  refreshThrottler.unregisterEventListener();
658  }
660  }
661 
662  @Override
663  protected Node createNodeForKey(BlackboardArtifact key) {
664  return new BlackboardArtifactNode(key);
665  }
666 
667  @Override
668  protected List<BlackboardArtifact> makeKeys() {
669  try {
670  List<? extends BlackboardArtifact> arts;
671  Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
672  switch (this.type.getCategory()) {
673 
674  case ANALYSIS_RESULT:
675  arts = (filteringDSObjId > 0)
676  ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
677  : blackboard.getAnalysisResultsByType(type.getTypeID());
678  break;
679  case DATA_ARTIFACT:
680  default:
681  arts = (filteringDSObjId > 0)
682  ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
683  : blackboard.getDataArtifacts(type.getTypeID());
684  break;
685  }
686 
687  for (BlackboardArtifact art : arts) {
688  //Cache attributes while we are off the EDT.
689  //See JIRA-5969
690  art.getAttributes();
691  }
692 
693  @SuppressWarnings("unchecked")
694  List<BlackboardArtifact> toRet = (List<BlackboardArtifact>) (List<?>) arts;
695  return toRet;
696  } catch (NoCurrentCaseException ex) {
697  logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
698  } catch (TskCoreException ex) {
699  logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
700  }
701  return Collections.emptyList();
702  }
703 
704  @Override
705  public void refresh() {
706  refresh(false);
707  }
708 
709  @Override
710  public boolean isRefreshRequired(PropertyChangeEvent evt) {
711  String eventType = evt.getPropertyName();
712  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
713 
720  try {
728  final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
729  if (null != event && event.getBlackboardArtifactType().equals(type)) {
730  return true;
731  }
732 
733  } catch (NoCurrentCaseException notUsed) {
737  }
738  }
739  return false;
740  }
741  }
742 }
static synchronized IngestManager getInstance()
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
Definition: Artifacts.java:75
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
final Set< BlackboardArtifact.Type > applicableTypes
Definition: Artifacts.java:135
boolean isRefreshRequired(PropertyChangeEvent evt)
Definition: Artifacts.java:710
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type...types)
Definition: Artifacts.java:469
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Feb 6 2024
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.