Autopsy  4.17.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.annotations.Beta;
22 import com.google.common.cache.Cache;
23 import com.google.common.cache.CacheBuilder;
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyChangeListener;
26 import java.lang.ref.WeakReference;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.EnumSet;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.MissingResourceException;
35 import java.util.Set;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.TimeUnit;
38 import java.util.logging.Level;
39 import java.util.stream.Collectors;
40 import javax.swing.Action;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.commons.lang3.tuple.Pair;
43 import org.openide.nodes.Sheet;
44 import org.openide.util.Lookup;
45 import org.openide.util.NbBundle;
46 import org.openide.util.WeakListeners;
47 import org.openide.util.lookup.Lookups;
64 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
66 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
70 import org.sleuthkit.datamodel.AbstractFile;
71 import org.sleuthkit.datamodel.BlackboardArtifact;
72 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
73 import org.sleuthkit.datamodel.BlackboardAttribute;
74 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
75 import org.sleuthkit.datamodel.Content;
76 import org.sleuthkit.datamodel.Tag;
77 import org.sleuthkit.datamodel.TskCoreException;
78 import org.sleuthkit.datamodel.TskData;
84 
89 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
90 
91  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
92 
93  /*
94  * Cache of Content objects used to avoid repeated trips to the case
95  * database to retrieve Content objects that are the source of multiple
96  * artifacts.
97  */
98  private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
99 
100  /*
101  * Case events that indicate an update to the node's property sheet may be
102  * required.
103  */
104  private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
111 
112  /*
113  * Artifact types for which the unique path of the artifact's source content
114  * should be displayed in the node's property sheet.
115  */
116  private static final Integer[] SHOW_UNIQUE_PATH = new Integer[]{
117  BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
118  BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
119  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
120  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
121  };
122 
123  /*
124  * Artifact types for which the file metadata of the artifact's source file
125  * should be displayed in the node's property sheet.
126  */
127  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
128  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
129  };
130 
131  private final BlackboardArtifact artifact;
132  private Content srcContent;
133  private volatile String translatedSourceName;
134 
135  /*
136  * A method has been provided to allow the injection of properties into this
137  * node for display in the node's property sheet, independent of the
138  * artifact the node represents.
139  */
140  private List<NodeProperty<? extends Object>> customProperties;
141 
142  private final PropertyChangeListener listener = new PropertyChangeListener() {
143  @Override
144  public void propertyChange(PropertyChangeEvent evt) {
145  String eventType = evt.getPropertyName();
146  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
148  if (event.getAddedTag().getArtifact().equals(artifact)) {
149  updateSheet();
150  }
151  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
153  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
154  updateSheet();
155  }
156  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
158  if (event.getAddedTag().getContent().equals(srcContent)) {
159  updateSheet();
160  }
161  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
163  if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
164  updateSheet();
165  }
166  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
168  if (event.getContentID() == srcContent.getId()) {
169  updateSheet();
170  }
171  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
172  if (evt.getNewValue() == null) {
173  /*
174  * The case has been closed.
175  */
177  contentCache.invalidateAll();
178  }
179  } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
180  SCOData scoData = (SCOData) evt.getNewValue();
181  if (scoData.getScoreAndDescription() != null) {
183  Bundle.BlackboardArtifactNode_createSheet_score_name(),
184  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
185  scoData.getScoreAndDescription().getRight(),
186  scoData.getScoreAndDescription().getLeft()));
187  }
188  if (scoData.getComment() != null) {
190  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
191  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
192  NO_DESCR, scoData.getComment()));
193  }
194  if (scoData.getCountAndDescription() != null) {
196  Bundle.BlackboardArtifactNode_createSheet_count_name(),
197  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
198  scoData.getCountAndDescription().getRight(),
199  scoData.getCountAndDescription().getLeft()));
200  }
201  } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
202  /*
203  * Replace the value of the Source File property with the
204  * translated name via setDisplayName (see note in createSheet),
205  * and put the untranslated name in the Original Name property
206  * and in the tooltip.
207  */
208  String originalName = evt.getOldValue().toString();
209  translatedSourceName = evt.getNewValue().toString();
210  setDisplayName(translatedSourceName);
211  setShortDescription(originalName);
213  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
214  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
215  NO_DESCR,
216  originalName));
217  }
218  }
219  };
220 
221  /*
222  * The node's event listener is wrapped in a weak reference that allows the
223  * node to be garbage collected when the NetBeans infrastructure discards
224  * it. If this is not done, it has been shown that strong references to the
225  * listener held by event publishers prevents garbage collection of this
226  * node.
227  */
228  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
229 
237  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
238  super(artifact, createLookup(artifact, false));
239  this.artifact = artifact;
240  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
241  if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
242  srcContent = lookupContent;
243  try {
244  /*
245  * Calling this getter causes the unique path of the source
246  * content to be cached in the Content object. This is
247  * advantageous as long as this node is constructed in a
248  * background thread instead of a UI thread.
249  */
250  srcContent.getUniquePath();
251  } catch (TskCoreException ex) {
252  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
253  }
254  break;
255  }
256  }
257  if (srcContent == null) {
258  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
259  }
260  setName(Long.toString(artifact.getArtifactID()));
261  String displayName = srcContent.getName();
262  setDisplayName(displayName);
263  setShortDescription(displayName);
264  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
266  }
267 
277  @Beta
278  public BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
279  super(artifact, createLookup(artifact, lookupIsAssociatedFile));
280  this.artifact = artifact;
281  try {
282  //The lookup for a file may or may not exist so we define the srcContent as the parent.
283  srcContent = artifact.getParent();
284  } catch (TskCoreException ex) {
285  logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
286  }
287  if (srcContent != null) {
288  try {
289  /*
290  * Calling this getter causes the unique path of the source
291  * content to be cached in the Content object. This is
292  * advantageous as long as this node is constructed in a
293  * background thread instead of a UI thread.
294  */
295  srcContent.getUniquePath();
296  } catch (TskCoreException ex) {
297  logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
298  }
299  } else {
300  throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
301  }
302  setName(Long.toString(artifact.getArtifactID()));
303  String displayName = srcContent.getName();
304  setDisplayName(displayName);
305  setShortDescription(displayName);
306  String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
307  setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
309  }
310 
317  public BlackboardArtifactNode(BlackboardArtifact artifact) {
318  this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
319  }
320 
329  private static Lookup createLookup(BlackboardArtifact artifact) {
330  final long objectID = artifact.getObjectID();
331  try {
332  Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
333  if (content == null) {
334  return Lookups.fixed(artifact);
335  } else {
336  return Lookups.fixed(artifact, content);
337  }
338  } catch (ExecutionException ex) {
339  logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
340  return Lookups.fixed(artifact);
341  }
342  }
343 
355  private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
356  Content content = null;
357  if (lookupIsAssociatedFile) {
358  try {
359  content = getPathIdFile(artifact);
360  } catch (ExecutionException ex) {
361  logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
362  content = null;
363  }
364  if (content == null) {
365  return Lookups.fixed(artifact);
366  } else {
367  return Lookups.fixed(artifact, content);
368  }
369  } else {
370  return createLookup(artifact);
371  }
372 
373  }
374 
388  private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
389  try {
390  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
391  if (attribute != null) {
392  return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
393  }
394  } catch (TskCoreException ex) {
395  logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
396  }
397  return null;
398  }
399 
409  @Override
410  protected void finalize() throws Throwable {
411  super.finalize();
413  }
414 
418  private void unregisterListener() {
420  }
421 
427  public BlackboardArtifact getArtifact() {
428  return this.artifact;
429  }
430 
431  @Override
432  public Action[] getActions(boolean context) {
433  List<Action> actionsList = new ArrayList<>();
434  actionsList.addAll(Arrays.asList(super.getActions(context)));
435 
436  /*
437  * If the artifact represented by this node has a timestamp, add an
438  * action to view it in the timeline.
439  */
440  try {
442  actionsList.add(new ViewArtifactInTimelineAction(artifact));
443  }
444  } catch (TskCoreException ex) {
445  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
446  }
447 
448  /*
449  * If the artifact represented by this node is linked to a file via a
450  * TSK_PATH_ID attribute, add an action to view the file in the
451  * timeline.
452  */
453  try {
454  AbstractFile linkedFile = findLinked(artifact);
455  if (linkedFile != null) {
456  actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
457  }
458  } catch (TskCoreException ex) {
459  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
460 
461  }
462 
463  /*
464  * If the source content of the artifact represented by this node is a
465  * file, add an action to view the file in the data source tree.
466  */
467  AbstractFile file = getLookup().lookup(AbstractFile.class
468  );
469  if (null != file) {
471  }
472 
473  return actionsList.toArray(new Action[actionsList.size()]);
474  }
475 
482  public String getSourceName() {
483  return srcContent.getName();
484  }
485 
486  @NbBundle.Messages({
487  "BlackboardArtifactNode.createSheet.srcFile.name=Source File",
488  "BlackboardArtifactNode.createSheet.srcFile.displayName=Source File",
489  "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
490  "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
491  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
492  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
493  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
494  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
495  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
496  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
497  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
498  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
499  "BlackboardArtifactNode.createSheet.path.displayName=Path",
500  "BlackboardArtifactNode.createSheet.path.name=Path"
501  })
502  @Override
503  protected Sheet createSheet() {
504  /*
505  * Create an empty property sheet.
506  */
507  Sheet sheet = super.createSheet();
508  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
509  if (sheetSet == null) {
510  sheetSet = Sheet.createPropertiesSet();
511  sheet.put(sheetSet);
512  }
513 
514  /*
515  * Add the name of the source content of the artifact represented by
516  * this node to the sheet. The value of this property is the same as the
517  * display name of the node and this a "special" property that displays
518  * the node's icon as well as the display name.
519  */
520  sheetSet.put(new NodeProperty<>(
521  Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
522  Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
523  NO_DESCR,
524  getDisplayName()));
525 
527  /*
528  * If machine translation is configured, add the original name of
529  * the of the source content of the artifact represented by this
530  * node to the sheet.
531  */
532  sheetSet.put(new NodeProperty<>(
533  Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
534  Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
535  NO_DESCR,
536  translatedSourceName != null ? srcContent.getName() : ""));
537  if (translatedSourceName == null) {
538  /*
539  * NOTE: The task makes its own weak reference to the listener.
540  */
541  new FileNameTransTask(srcContent.getName(), this, listener).submit();
542  }
543  }
544 
546  /*
547  * Add S(core), C(omments), and O(ther occurences) columns to the
548  * sheet and start a background task to compute the value of these
549  * properties for the artifact represented by this node. The task
550  * will fire a PropertyChangeEvent when the computation is completed
551  * and this node's PropertyChangeListener will update the sheet.
552  */
553  sheetSet.put(new NodeProperty<>(
554  Bundle.BlackboardArtifactNode_createSheet_score_name(),
555  Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
557  ""));
558  sheetSet.put(new NodeProperty<>(
559  Bundle.BlackboardArtifactNode_createSheet_comment_name(),
560  Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
562  ""));
564  sheetSet.put(new NodeProperty<>(
565  Bundle.BlackboardArtifactNode_createSheet_count_name(),
566  Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
568  ""));
569  }
570  backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
571  }
572 
573  /*
574  * If the artifact represented by this node is an interesting artifact
575  * hit, add the type and description of the interesting artifact to the
576  * sheet.
577  */
578  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
579  try {
580  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
581  if (attribute != null) {
582  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
583  sheetSet.put(new NodeProperty<>(
584  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
585  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
586  NO_DESCR,
587  associatedArtifact.getDisplayName()));
588  sheetSet.put(new NodeProperty<>(
589  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
590  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
591  NO_DESCR,
592  associatedArtifact.getShortDescription()));
593  }
594  } catch (TskCoreException | NoCurrentCaseException ex) {
595  logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
596  }
597  }
598 
599  /*
600  * Add the attributes of the artifact represented by this node to the
601  * sheet.
602  */
603  Map<String, Object> map = new LinkedHashMap<>();
604  fillPropertyMap(map, artifact);
605  for (Map.Entry<String, Object> entry : map.entrySet()) {
606  sheetSet.put(new NodeProperty<>(entry.getKey(),
607  entry.getKey(),
608  NO_DESCR,
609  entry.getValue()));
610  }
611 
612  /*
613  * Add any "custom properties" for the node to the sheet.
614  */
615  if (customProperties != null) {
616  for (NodeProperty<? extends Object> np : customProperties) {
617  sheetSet.put(np);
618  }
619  }
620 
621  /*
622  * If the artifact represented by this node is a file extension mismatch
623  * artifact, add the extension and type of the artifact's source file to
624  * the sheet.
625  */
626  final int artifactTypeId = artifact.getArtifactTypeID();
627  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
628  String ext = ""; //NON-NLS
629  String actualMimeType = ""; //NON-NLS
630  if (srcContent instanceof AbstractFile) {
631  AbstractFile file = (AbstractFile) srcContent;
632  ext = file.getNameExtension();
633  actualMimeType = file.getMIMEType();
634  if (actualMimeType == null) {
635  actualMimeType = ""; //NON-NLS
636 
637  }
638  }
639  sheetSet.put(new NodeProperty<>(
640  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
641  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
642  NO_DESCR,
643  ext));
644  sheetSet.put(new NodeProperty<>(
645  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
646  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
647  NO_DESCR,
648  actualMimeType));
649  }
650 
651  /*
652  * If the type of the artifact represented by this node dictates the
653  * addition of the source content's unique path, add it to the sheet.
654  */
655  if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
656  String sourcePath = ""; //NON-NLS
657  try {
658  sourcePath = srcContent.getUniquePath();
659  } catch (TskCoreException ex) {
660  logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
661 
662  }
663 
664  if (sourcePath.isEmpty() == false) {
665  sheetSet.put(new NodeProperty<>(
666  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
667  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
668  NO_DESCR,
669  sourcePath));
670  }
671 
672  /*
673  * If the type of the artifact represented by this node dictates the
674  * addition of the source content's file metadata, add it to the
675  * sheet. Otherwise, add the data source to the sheet.
676  */
677  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
678  AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
679  sheetSet.put(new NodeProperty<>(
680  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
681  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
682  "",
683  file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file)));
684  sheetSet.put(new NodeProperty<>(
685  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
686  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
687  "",
688  file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file)));
689  sheetSet.put(new NodeProperty<>(
690  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
691  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
692  "",
693  file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file)));
694  sheetSet.put(new NodeProperty<>(
695  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
696  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
697  "",
698  file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file)));
699  sheetSet.put(new NodeProperty<>(
700  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
701  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
702  "",
703  file == null ? "" : file.getSize()));
704  sheetSet.put(new NodeProperty<>(
705  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
706  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
707  "",
708  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
709  }
710  } else {
711  String dataSourceStr = "";
712  try {
713  Content dataSource = srcContent.getDataSource();
714  if (dataSource != null) {
715  dataSourceStr = dataSource.getName();
716  } else {
717  dataSourceStr = getRootAncestorName();
718  }
719  } catch (TskCoreException ex) {
720  logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
721 
722  }
723 
724  if (dataSourceStr.isEmpty() == false) {
725  sheetSet.put(new NodeProperty<>(
726  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
727  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
728  NO_DESCR,
729  dataSourceStr));
730  }
731  }
732 
733  /*
734  * If the artifact represented by this node is an EXIF artifact, add the
735  * source file size and path to the sheet.
736  */
737  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
738  long size = 0;
739  String path = ""; //NON-NLS
740  if (srcContent instanceof AbstractFile) {
741  AbstractFile af = (AbstractFile) srcContent;
742  size = af.getSize();
743  try {
744  path = af.getUniquePath();
745  } catch (TskCoreException ex) {
746  path = af.getParentPath();
747 
748  }
749  }
750  sheetSet.put(new NodeProperty<>(
751  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
752  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
753  NO_DESCR,
754  size));
755  sheetSet
756  .put(new NodeProperty<>(
757  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
758  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
759  NO_DESCR,
760  path));
761  }
762 
763  return sheet;
764  }
765 
772  @Override
773  protected final List<Tag> getAllTagsFromDatabase() {
774  List<Tag> tags = new ArrayList<>();
775  try {
778  } catch (TskCoreException | NoCurrentCaseException ex) {
779  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
780  }
781  return tags;
782  }
783 
792  @Override
794  CorrelationAttributeInstance correlationAttribute = null;
795  if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
796  correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
797  }
798  return correlationAttribute;
799  }
800 
816  @Override
818 
819  /*
820  * Has a tag with a comment been applied to the artifact or its source
821  * content?
822  */
824  for (Tag tag : tags) {
825  if (!StringUtils.isBlank(tag.getComment())) {
826  status = HasCommentStatus.TAG_COMMENT;
827  break;
828  }
829  }
830 
831  /*
832  * Does the given correlation attribute instance have a comment in the
833  * central repository?
834  */
835  if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
836  if (status == HasCommentStatus.TAG_COMMENT) {
838  } else {
839  status = HasCommentStatus.CR_COMMENT;
840  }
841  }
842 
843  return status;
844  }
845 
866  @Override
867  protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
868  /*
869  * Is the artifact's source content marked as notable?
870  */
871  Score score = Score.NO_SCORE;
872  String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
873  if (srcContent instanceof AbstractFile) {
874  if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
875  score = Score.NOTABLE_SCORE;
876  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
877  }
878  }
879 
880  /*
881  * If the artifact is a hash set hit, is the hash set a notable hashes
882  * hash set?
883  */
884  if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
885  try {
886  BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
888  for (HashDbManager.HashDb hashDb : notableHashsets) {
889  if (hashDb.getHashSetName().equals(attr.getValueString())) {
890  score = Score.NOTABLE_SCORE;
891  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
892  break;
893  }
894  }
895  } catch (TskCoreException ex) {
896  logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute for TSK_HASHSET_HIT artifact (artifact objID={0})", artifact.getId()), ex);
897  }
898  }
899 
900  /*
901  * Is the artifact's source content notable?
902  */
903  if (score == Score.NO_SCORE) {
904  try {
905  if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
906  score = Score.INTERESTING_SCORE;
907  description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description();
908  }
909  } catch (TskCoreException ex) {
910  logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts for source content (artifact objID={0})", artifact.getId()), ex);
911  }
912  }
913 
914  /*
915  * Analyze any tags applied to the artifact or its source content. If
916  * there are tags, tha artifact is at least interesting. If one of the
917  * tags is a notable tag, the artifact is notable.
918  */
919  if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
920  score = Score.INTERESTING_SCORE;
921  description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
922  for (Tag tag : tags) {
923  if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
924  score = Score.NOTABLE_SCORE;
925  description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description();
926  break;
927  }
928  }
929  }
930 
931  return Pair.of(score, description);
932  }
933 
952  @Override
953  protected Pair<Long, String> getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription) {
954  Long count = -1L;
955  String description = defaultDescription;
956  try {
957  if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) {
959  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName());
960  } else if (corrAttrType != null) {
961  description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
962  }
963  } catch (CentralRepoException ex) {
964  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);
966  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);
967  }
968  return Pair.of(count, description);
969  }
970 
974  private void updateSheet() {
975  this.setSheet(createSheet());
976  }
977 
984  private String getRootAncestorName() {
985  String parentName = srcContent.getName();
986  Content parent = srcContent;
987  try {
988  while ((parent = parent.getParent()) != null) {
989  parentName = parent.getName();
990  }
991  } catch (TskCoreException ex) {
992  logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
993  return "";
994  }
995  return parentName;
996  }
997 
1004  public void addNodeProperty(NodeProperty<?> property) {
1005  if (customProperties == null) {
1006  customProperties = new ArrayList<>();
1007  }
1008  customProperties.add(property);
1009  }
1010 
1019  @SuppressWarnings("deprecation")
1020  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
1021  try {
1022  for (BlackboardAttribute attribute : artifact.getAttributes()) {
1023  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1024  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
1025  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
1026  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
1027  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
1028  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
1029  || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1030  /*
1031  * Do nothing.
1032  */
1033  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
1034  addEmailMsgProperty(map, attribute);
1035  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1036  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
1037  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
1038  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
1039  /*
1040  * The truncation of text attributes appears to have been
1041  * motivated by the statement that "RegRipper output would
1042  * often cause the UI to get a black line accross it and
1043  * hang if you hovered over large output or selected it.
1044  * This reduces the amount of data in the table. Could
1045  * consider doing this for all fields in the UI."
1046  */
1047  String value = attribute.getDisplayString();
1048  if (value.length() > 512) {
1049  value = value.substring(0, 512);
1050  }
1051  map.put(attribute.getAttributeType().getDisplayName(), value);
1052  } else {
1053  switch (attribute.getAttributeType().getValueType()) {
1054  case INTEGER:
1055  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
1056  break;
1057  case DOUBLE:
1058  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
1059  break;
1060  case LONG:
1061  map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
1062  break;
1063  default:
1064  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1065 
1066  }
1067 
1068  }
1069  }
1070  } catch (TskCoreException ex) {
1071  logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1072  }
1073  }
1074 
1084  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
1085  final int attributeTypeID = attribute.getAttributeType().getTypeID();
1086  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
1087  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
1088  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
1089  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
1090  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
1091  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
1092  /*
1093  * Do nothing.
1094  */
1095  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
1096  String value = attribute.getDisplayString();
1097  if (value.length() > 160) {
1098  value = value.substring(0, 160) + "...";
1099  }
1100  map.put(attribute.getAttributeType().getDisplayName(), value);
1101  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1102  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
1103  } else {
1104  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1105  }
1106  }
1107 
1108  @Override
1109  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1110  return visitor.visit(this);
1111  }
1112 
1113  @Override
1114  public boolean isLeafTypeNode() {
1115  return true;
1116  }
1117 
1118  @Override
1119  public String getItemType() {
1120  return getClass().getName();
1121  }
1122 
1123  @Override
1124  public <T> T accept(ContentNodeVisitor<T> visitor) {
1125  return visitor.visit(this);
1126  }
1127 
1140  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1141  "BlackboardArtifactNode.createSheet.score.displayName=S",
1142  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1143  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1144  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1145  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1146  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1147  @Deprecated
1148  protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1149  Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
1150  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1151  }
1152 
1162  @NbBundle.Messages({
1163  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1164  )
1165  @Deprecated
1166  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1167  List<Tag> tags = new ArrayList<>();
1168  try {
1171  } catch (TskCoreException | NoCurrentCaseException ex) {
1172  logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1173  }
1174  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1175  }
1176 
1188  @Deprecated
1189  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1190  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1191  }
1192 
1205  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1206  "BlackboardArtifactNode.createSheet.count.displayName=O",
1207  "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1208  "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1209  "# {0} - occurrenceCount",
1210  "# {1} - attributeType",
1211  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1212  @Deprecated
1213  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1214  Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1215  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1216  }
1217 
1232  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1233  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1234  @Deprecated
1235  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1236  HasCommentStatus status = getCommentProperty(tags, attribute);
1237  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1238  }
1239 
1240 }
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)
static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile)
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 Content getPathIdFile(BlackboardArtifact artifact)
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:491
BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:536
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value)
static ViewFileInTimelineAction createViewFileAction(AbstractFile file)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

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