Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
BlackboardArtifactNode.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-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.datamodel;
20 
21 import com.google.common.cache.Cache;
22 import com.google.common.cache.CacheBuilder;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.lang.ref.WeakReference;
26 import java.text.MessageFormat;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.EnumSet;
30 import java.util.LinkedHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.MissingResourceException;
34 import java.util.Set;
35 import java.util.concurrent.ExecutionException;
36 import java.util.concurrent.TimeUnit;
37 import java.util.logging.Level;
38 import java.util.stream.Collectors;
39 import javax.swing.Action;
40 import org.apache.commons.lang3.StringUtils;
41 import org.apache.commons.lang3.tuple.Pair;
42 import org.openide.nodes.Sheet;
43 import org.openide.util.Lookup;
44 import org.openide.util.NbBundle;
45 import org.openide.util.WeakListeners;
46 import org.openide.util.lookup.Lookups;
63 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
65 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
69 import org.sleuthkit.datamodel.AbstractFile;
70 import org.sleuthkit.datamodel.BlackboardArtifact;
71 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
72 import org.sleuthkit.datamodel.BlackboardAttribute;
73 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
74 import org.sleuthkit.datamodel.Content;
75 import org.sleuthkit.datamodel.Tag;
76 import org.sleuthkit.datamodel.TskCoreException;
77 import org.sleuthkit.datamodel.TskData;
83 
88 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
89 
90  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
91 
92  /*
93  * Cache of Content objects used to avoid repeated trips to the case
94  * database to retrieve Content objects that are the source of multiple
95  * artifacts.
96  */
97  private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
98 
99  /*
100  * Case events that indicate an update to the node's property sheet may be
101  * required.
102  */
103  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
110 
111  /*
112  * Artifact types for which the unique path of the artifact's source content
113  * should be displayed in the node's property sheet.
114  */
115  private static final Integer[] SHOW_UNIQUE_PATH = new Integer[]{
116  BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
117  BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
118  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
119  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
120  };
121 
122  /*
123  * Artifact types for which the file metadata of the artifact's source file
124  * should be displayed in the node's property sheet.
125  */
126  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
127  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
128  };
129 
130  private final BlackboardArtifact artifact;
131  private Content srcContent;
132  private volatile String translatedSourceName;
133 
134  /*
135  * A method has been provided to allow the injection of properties into this
136  * node for display in the node's property sheet, independent of the
137  * artifact the node represents.
138  */
139  private List<NodeProperty<? extends Object>> customProperties;
140 
141  private final PropertyChangeListener listener = new PropertyChangeListener() {
142  @Override
143  public void propertyChange(PropertyChangeEvent evt) {
144  String eventType = evt.getPropertyName();
145  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
147  if (event.getAddedTag().getArtifact().equals(artifact)) {
148  updateSheet();
149  }
150  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
152  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
153  updateSheet();
154  }
155  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
157  if (event.getAddedTag().getContent().equals(srcContent)) {
158  updateSheet();
159  }
160  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
162  if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
163  updateSheet();
164  }
165  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
167  if (event.getContentID() == srcContent.getId()) {
168  updateSheet();
169  }
170  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
171  if (evt.getNewValue() == null) {
172  /*
173  * The case has been closed.
174  */
176  contentCache.invalidateAll();
177  }
178  } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
179  SCOData scoData = (SCOData) evt.getNewValue();
180  if (scoData.getScoreAndDescription() != null) {
182  Bundle.BlackboardArtifactNode_createSheet_score_name(),
183  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
184  scoData.getScoreAndDescription().getRight(),
185  scoData.getScoreAndDescription().getLeft()));
186  }
187  if (scoData.getComment() != null) {
189  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
190  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
191  NO_DESCR, scoData.getComment()));
192  }
193  if (scoData.getCountAndDescription() != null) {
195  Bundle.BlackboardArtifactNode_createSheet_count_name(),
196  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
197  scoData.getCountAndDescription().getRight(),
198  scoData.getCountAndDescription().getLeft()));
199  }
200  } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
201  /*
202  * Replace the value of the Source File property with the
203  * translated name via setDisplayName (see note in createSheet),
204  * and put the untranslated name in the Original Name property
205  * and in the tooltip.
206  */
207  String originalName = evt.getOldValue().toString();
208  translatedSourceName = evt.getNewValue().toString();
209  setDisplayName(translatedSourceName);
210  setShortDescription(originalName);
212  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
213  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
214  NO_DESCR,
215  originalName));
216  }
217  }
218  };
219 
220  /*
221  * The node's event listener is wrapped in a weak reference that allows the
222  * node to be garbage collected when the NetBeans infrastructure discards
223  * it. If this is not done, it has been shown that strong references to the
224  * listener held by event publishers prevents garbage collection of this
225  * node.
226  */
227  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
228 
236  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
237  super(artifact, createLookup(artifact));
238  this.artifact = artifact;
239  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
240  if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
241  srcContent = lookupContent;
242  try {
243  /*
244  * Calling this getter causes the unique path of the source
245  * content to be cached in the Content object. This is
246  * advantageous as long as this node is constructed in a
247  * background thread instead of a UI thread.
248  */
249  srcContent.getUniquePath();
250  } catch (TskCoreException ex) {
251  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
252  }
253  break;
254  }
255  }
256  if (srcContent == null) {
257  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
258  }
259  setName(Long.toString(artifact.getArtifactID()));
260  String displayName = srcContent.getName();
261  setDisplayName(displayName);
262  setShortDescription(displayName);
263  setIconBaseWithExtension(iconPath);
265  }
266 
273  public BlackboardArtifactNode(BlackboardArtifact artifact) {
274  this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
275  }
276 
285  private static Lookup createLookup(BlackboardArtifact artifact) {
286  final long objectID = artifact.getObjectID();
287  try {
288  Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
289  if (content == null) {
290  return Lookups.fixed(artifact);
291  } else {
292  return Lookups.fixed(artifact, content);
293  }
294  } catch (ExecutionException ex) {
295  logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
296  return Lookups.fixed(artifact);
297  }
298  }
299 
309  @Override
310  protected void finalize() throws Throwable {
311  super.finalize();
313  }
314 
318  private void unregisterListener() {
320  }
321 
327  public BlackboardArtifact getArtifact() {
328  return this.artifact;
329  }
330 
331  @Override
332  public Action[] getActions(boolean context) {
333  List<Action> actionsList = new ArrayList<>();
334  actionsList.addAll(Arrays.asList(super.getActions(context)));
335 
336  /*
337  * If the artifact represented by this node has a timestamp, add an
338  * action to view it in the timeline.
339  */
340  try {
342  actionsList.add(new ViewArtifactInTimelineAction(artifact));
343  }
344  } catch (TskCoreException ex) {
345  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
346  }
347 
348  /*
349  * If the artifact represented by this node is linked to a file via a
350  * TSK_PATH_ID attribute, add an action to view the file in the
351  * timeline.
352  */
353  try {
354  AbstractFile linkedFile = findLinked(artifact);
355  if (linkedFile != null) {
356  actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
357  }
358  } catch (TskCoreException ex) {
359  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
360 
361  }
362 
363  /*
364  * If the source content of the artifact represented by this node is a
365  * file, add an action to view the file in the data source tree.
366  */
367  AbstractFile file = getLookup().lookup(AbstractFile.class
368  );
369  if (null != file) {
371  }
372 
373  return actionsList.toArray(new Action[actionsList.size()]);
374  }
375 
382  public String getSourceName() {
383  return srcContent.getName();
384  }
385 
386  @NbBundle.Messages({
387  "BlackboardArtifactNode.createSheet.srcFile.name=Source File",
388  "BlackboardArtifactNode.createSheet.srcFile.displayName=Source File",
389  "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
390  "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
391  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
392  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
393  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
394  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
395  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
396  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
397  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
398  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
399  "BlackboardArtifactNode.createSheet.path.displayName=Path",
400  "BlackboardArtifactNode.createSheet.path.name=Path"
401  })
402  @Override
403  protected Sheet createSheet() {
404  /*
405  * Create an empty property sheet.
406  */
407  Sheet sheet = super.createSheet();
408  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
409  if (sheetSet == null) {
410  sheetSet = Sheet.createPropertiesSet();
411  sheet.put(sheetSet);
412  }
413 
414  /*
415  * Add the name of the source content of the artifact represented by
416  * this node to the sheet. The value of this property is the same as the
417  * display name of the node and this a "special" property that displays
418  * the node's icon as well as the display name.
419  */
420  sheetSet.put(new NodeProperty<>(
421  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
422  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
423  NO_DESCR,
424  getDisplayName()));
425 
427  /*
428  * If machine translation is configured, add the original name of
429  * the of the source content of the artifact represented by this
430  * node to the sheet.
431  */
432  sheetSet.put(new NodeProperty<>(
433  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
434  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
435  NO_DESCR,
436  translatedSourceName != null ? srcContent.getName() : ""));
437  if (translatedSourceName == null) {
438  /*
439  * NOTE: The task makes its own weak reference to the listener.
440  */
441  new FileNameTransTask(srcContent.getName(), this, listener).submit();
442  }
443  }
444 
446  /*
447  * Add S(core), C(omments), and O(ther occurences) columns to the
448  * sheet and start a background task to compute the value of these
449  * properties for the artifact represented by this node. The task
450  * will fire a PropertyChangeEvent when the computation is completed
451  * and this node's PropertyChangeListener will update the sheet.
452  */
453  sheetSet.put(new NodeProperty<>(
454  Bundle.BlackboardArtifactNode_createSheet_score_name(),
455  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
457  ""));
458  sheetSet.put(new NodeProperty<>(
459  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
460  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
462  ""));
464  sheetSet.put(new NodeProperty<>(
465  Bundle.BlackboardArtifactNode_createSheet_count_name(),
466  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
468  ""));
469  }
470  backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
471  }
472 
473  /*
474  * If the artifact represented by this node is an interesting artifact
475  * hit, add the type and description of the interesting artifact to the
476  * sheet.
477  */
478  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
479  try {
480  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
481  if (attribute != null) {
482  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
483  sheetSet.put(new NodeProperty<>(
484  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
485  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
486  NO_DESCR,
487  associatedArtifact.getDisplayName()));
488  sheetSet.put(new NodeProperty<>(
489  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
490  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
491  NO_DESCR,
492  associatedArtifact.getShortDescription()));
493  }
494  } catch (TskCoreException | NoCurrentCaseException ex) {
495  logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
496  }
497  }
498 
499  /*
500  * Add the attributes of the artifact represented by this node to the
501  * sheet.
502  */
503  Map<String, Object> map = new LinkedHashMap<>();
504  fillPropertyMap(map, artifact);
505  for (Map.Entry<String, Object> entry : map.entrySet()) {
506  sheetSet.put(new NodeProperty<>(entry.getKey(),
507  entry.getKey(),
508  NO_DESCR,
509  entry.getValue()));
510  }
511 
512  /*
513  * Add any "custom properties" for the node to the sheet.
514  */
515  if (customProperties != null) {
516  for (NodeProperty<? extends Object> np : customProperties) {
517  sheetSet.put(np);
518  }
519  }
520 
521  /*
522  * If the artifact represented by this node is a file extension mismatch
523  * artifact, add the extension and type of the artifact's source file to
524  * the sheet.
525  */
526  final int artifactTypeId = artifact.getArtifactTypeID();
527  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
528  String ext = ""; //NON-NLS
529  String actualMimeType = ""; //NON-NLS
530  if (srcContent instanceof AbstractFile) {
531  AbstractFile file = (AbstractFile) srcContent;
532  ext = file.getNameExtension();
533  actualMimeType = file.getMIMEType();
534  if (actualMimeType == null) {
535  actualMimeType = ""; //NON-NLS
536 
537  }
538  }
539  sheetSet.put(new NodeProperty<>(
540  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
541  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
542  NO_DESCR,
543  ext));
544  sheetSet.put(new NodeProperty<>(
545  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
546  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
547  NO_DESCR,
548  actualMimeType));
549  }
550 
551  /*
552  * If the type of the artifact represented by this node dictates the
553  * addition of the source content's unique path, add it to the sheet.
554  */
555  if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
556  String sourcePath = ""; //NON-NLS
557  try {
558  sourcePath = srcContent.getUniquePath();
559  } catch (TskCoreException ex) {
560  logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
561 
562  }
563 
564  if (sourcePath.isEmpty() == false) {
565  sheetSet.put(new NodeProperty<>(
566  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
567  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
568  NO_DESCR,
569  sourcePath));
570  }
571 
572  /*
573  * If the type of the artifact represented by this node dictates the
574  * addition of the source content's file metadata, add it to the
575  * sheet. Otherwise, add the data source to the sheet.
576  */
577  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
578  AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
579  sheetSet.put(new NodeProperty<>(
580  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
581  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
582  "",
583  file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file)));
584  sheetSet.put(new NodeProperty<>(
585  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
586  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
587  "",
588  file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file)));
589  sheetSet.put(new NodeProperty<>(
590  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
591  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
592  "",
593  file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file)));
594  sheetSet.put(new NodeProperty<>(
595  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
596  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
597  "",
598  file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file)));
599  sheetSet.put(new NodeProperty<>(
600  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
601  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
602  "",
603  file == null ? "" : file.getSize()));
604  sheetSet.put(new NodeProperty<>(
605  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
606  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
607  "",
608  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
609  }
610  } else {
611  String dataSourceStr = "";
612  try {
613  Content dataSource = srcContent.getDataSource();
614  if (dataSource != null) {
615  dataSourceStr = dataSource.getName();
616  } else {
617  dataSourceStr = getRootAncestorName();
618  }
619  } catch (TskCoreException ex) {
620  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
621 
622  }
623 
624  if (dataSourceStr.isEmpty() == false) {
625  sheetSet.put(new NodeProperty<>(
626  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
627  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
628  NO_DESCR,
629  dataSourceStr));
630  }
631  }
632 
633  /*
634  * If the artifact represented by this node is an EXIF artifact, add the
635  * source file size and path to the sheet.
636  */
637  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
638  long size = 0;
639  String path = ""; //NON-NLS
640  if (srcContent instanceof AbstractFile) {
641  AbstractFile af = (AbstractFile) srcContent;
642  size = af.getSize();
643  try {
644  path = af.getUniquePath();
645  } catch (TskCoreException ex) {
646  path = af.getParentPath();
647 
648  }
649  }
650  sheetSet.put(new NodeProperty<>(
651  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
652  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
653  NO_DESCR,
654  size));
655  sheetSet
656  .put(new NodeProperty<>(
657  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
658  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
659  NO_DESCR,
660  path));
661  }
662 
663  return sheet;
664  }
665 
672  @Override
673  protected final List<Tag> getAllTagsFromDatabase() {
674  List<Tag> tags = new ArrayList<>();
675  try {
678  } catch (TskCoreException | NoCurrentCaseException ex) {
679  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
680  }
681  return tags;
682  }
683 
692  @Override
694  CorrelationAttributeInstance correlationAttribute = null;
695  if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
696  correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
697  }
698  return correlationAttribute;
699  }
700 
716  @Override
718 
719  /*
720  * Has a tag with a comment been applied to the artifact or its source
721  * content?
722  */
724  for (Tag tag : tags) {
725  if (!StringUtils.isBlank(tag.getComment())) {
726  status = HasCommentStatus.TAG_COMMENT;
727  break;
728  }
729  }
730 
731  /*
732  * Does the given correlation attribute instance have a comment in the
733  * central repository?
734  */
735  if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
736  if (status == HasCommentStatus.TAG_COMMENT) {
738  } else {
739  status = HasCommentStatus.CR_COMMENT;
740  }
741  }
742 
743  return status;
744  }
745 
766  @Override
767  protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
768  /*
769  * Is the artifact's source content marked as notable?
770  */
771  Score score = Score.NO_SCORE;
772  String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
773  if (srcContent instanceof AbstractFile) {
774  if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
775  score = Score.NOTABLE_SCORE;
776  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
777  }
778  }
779 
780  /*
781  * If the artifact is a hash set hit, is the hash set a notable hashes
782  * hash set?
783  */
784  if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
785  try {
786  BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
788  for (HashDbManager.HashDb hashDb : notableHashsets) {
789  if (hashDb.getHashSetName().equals(attr.getValueString())) {
790  score = Score.NOTABLE_SCORE;
791  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
792  break;
793  }
794  }
795  } catch (TskCoreException ex) {
796  logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute for TSK_HASHSET_HIT artifact (artifact objID={0})", artifact.getId()), ex);
797  }
798  }
799 
800  /*
801  * Is the artifact's source content notable?
802  */
803  if (score == Score.NO_SCORE) {
804  try {
805  if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
806  score = Score.INTERESTING_SCORE;
807  description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description();
808  }
809  } catch (TskCoreException ex) {
810  logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts for source content (artifact objID={0})", artifact.getId()), ex);
811  }
812  }
813 
814  /*
815  * Analyze any tags applied to the artifact or its source content. If
816  * there are tags, tha artifact is at least interesting. If one of the
817  * tags is a notable tag, the artifact is notable.
818  */
819  if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
820  score = Score.INTERESTING_SCORE;
821  description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
822  for (Tag tag : tags) {
823  if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
824  score = Score.NOTABLE_SCORE;
825  description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description();
826  break;
827  }
828  }
829  }
830 
831  return Pair.of(score, description);
832  }
833 
852  @Override
853  protected Pair<Long, String> getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription) {
854  Long count = -1L;
855  String description = defaultDescription;
856  try {
857  if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) {
859  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName());
860  } else if (corrAttrType != null) {
861  description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
862  }
863  } catch (CentralRepoException ex) {
864  logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), corrAttrType, attributeValue), ex);
866  logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), corrAttrType, attributeValue), ex);
867  }
868  return Pair.of(count, description);
869  }
870 
874  private void updateSheet() {
875  this.setSheet(createSheet());
876  }
877 
884  private String getRootAncestorName() {
885  String parentName = srcContent.getName();
886  Content parent = srcContent;
887  try {
888  while ((parent = parent.getParent()) != null) {
889  parentName = parent.getName();
890  }
891  } catch (TskCoreException ex) {
892  logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
893  return "";
894  }
895  return parentName;
896  }
897 
904  public void addNodeProperty(NodeProperty<?> property) {
905  if (customProperties == null) {
906  customProperties = new ArrayList<>();
907  }
908  customProperties.add(property);
909  }
910 
919  @SuppressWarnings("deprecation")
920  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
921  try {
922  for (BlackboardAttribute attribute : artifact.getAttributes()) {
923  final int attributeTypeID = attribute.getAttributeType().getTypeID();
924  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
925  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
926  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
927  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
928  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
929  || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
930  /*
931  * Do nothing.
932  */
933  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
934  addEmailMsgProperty(map, attribute);
935  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
936  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
937  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
938  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
939  /*
940  * The truncation of text attributes appears to have been
941  * motivated by the statement that "RegRipper output would
942  * often cause the UI to get a black line accross it and
943  * hang if you hovered over large output or selected it.
944  * This reduces the amount of data in the table. Could
945  * consider doing this for all fields in the UI."
946  */
947  String value = attribute.getDisplayString();
948  if (value.length() > 512) {
949  value = value.substring(0, 512);
950  }
951  map.put(attribute.getAttributeType().getDisplayName(), value);
952  } else {
953  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
954  }
955  }
956  } catch (TskCoreException ex) {
957  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
958  }
959  }
960 
970  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
971  final int attributeTypeID = attribute.getAttributeType().getTypeID();
972  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
973  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
974  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
975  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
976  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
977  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
978  /*
979  * Do nothing.
980  */
981  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
982  String value = attribute.getDisplayString();
983  if (value.length() > 160) {
984  value = value.substring(0, 160) + "...";
985  }
986  map.put(attribute.getAttributeType().getDisplayName(), value);
987  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
988  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
989  } else {
990  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
991  }
992  }
993 
994  @Override
995  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
996  return visitor.visit(this);
997  }
998 
999  @Override
1000  public boolean isLeafTypeNode() {
1001  return true;
1002  }
1003 
1004  @Override
1005  public String getItemType() {
1006  return getClass().getName();
1007  }
1008 
1009  @Override
1010  public <T> T accept(ContentNodeVisitor<T> visitor) {
1011  return visitor.visit(this);
1012  }
1013 
1026  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1027  "BlackboardArtifactNode.createSheet.score.displayName=S",
1028  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1029  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1030  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1031  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1032  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1033  @Deprecated
1034  protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1035  Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
1036  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1037  }
1038 
1048  @NbBundle.Messages({
1049  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1050  )
1051  @Deprecated
1052  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1053  List<Tag> tags = new ArrayList<>();
1054  try {
1057  } catch (TskCoreException | NoCurrentCaseException ex) {
1058  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1059  }
1060  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1061  }
1062 
1074  @Deprecated
1075  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1076  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1077  }
1078 
1091  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1092  "BlackboardArtifactNode.createSheet.count.displayName=O",
1093  "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1094  "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1095  "# {0} - occurrenceCount",
1096  "# {1} - attributeType",
1097  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1098  @Deprecated
1099  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1100  Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1101  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1102  }
1103 
1118  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1119  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1120  @Deprecated
1121  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1122  HasCommentStatus status = getCommentProperty(tags, attribute);
1123  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1124  }
1125 
1126 }
Pair< DataResultViewerTable.Score, String > getScorePropertyAndDescription(List< Tag > tags)
final void addTagProperty(Sheet.Set sheetSet, List< Tag > tags)
void fillPropertyMap(Map< String, Object > map, BlackboardArtifact artifact)
static String getStringTime(long epochSeconds, TimeZone tzone)
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
List< ContentTag > getContentTagsByContent(Content content)
static Lookup createLookup(BlackboardArtifact artifact)
Pair< Long, String > getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription)
static ViewFileInTimelineAction createViewSourceFileAction(AbstractFile file)
final void addScorePropertyAndDescription(Sheet.Set sheetSet, List< Tag > tags)
static CorrelationAttributeInstance getCorrAttrForFile(AbstractFile file)
void addEmailMsgProperty(Map< String, Object > map, BlackboardAttribute attribute)
final void addCommentProperty(Sheet.Set sheetSet, List< Tag > tags, CorrelationAttributeInstance attribute)
final CorrelationAttributeInstance getCorrelationAttributeInstance()
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, CorrelationAttributeInstance attribute)
final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:486
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:531
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value)
static ViewFileInTimelineAction createViewFileAction(AbstractFile file)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

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