Autopsy  4.10.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 2011-2018 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.text.MessageFormat;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.EnumSet;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.MissingResourceException;
33 import java.util.Set;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.TimeUnit;
36 import java.util.logging.Level;
37 import java.util.stream.Collectors;
38 import javax.swing.Action;
39 import org.apache.commons.lang3.StringUtils;
40 import org.openide.nodes.Sheet;
41 import org.openide.util.Lookup;
42 import org.openide.util.NbBundle;
43 import org.openide.util.WeakListeners;
44 import org.openide.util.lookup.Lookups;
61 import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
66 import org.sleuthkit.datamodel.AbstractFile;
67 import org.sleuthkit.datamodel.BlackboardArtifact;
68 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
69 import org.sleuthkit.datamodel.BlackboardAttribute;
70 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
71 import org.sleuthkit.datamodel.Content;
72 import org.sleuthkit.datamodel.Tag;
73 import org.sleuthkit.datamodel.TskCoreException;
74 import org.sleuthkit.datamodel.TskData;
75 
80 public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
81 
82  private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
89 
90  private static Cache<Long, Content> contentCache = CacheBuilder.newBuilder()
91  .expireAfterWrite(1, TimeUnit.MINUTES).
92  build();
93 
94  private final BlackboardArtifact artifact;
95  private Content associated = null;
96 
97  private List<NodeProperty<? extends Object>> customProperties;
98 
99  private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
100 
101 
102  /*
103  * Artifact types which should have the full unique path of the associated
104  * content as a property.
105  */
106  private static final Integer[] SHOW_UNIQUE_PATH = new Integer[]{
107  BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
108  BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
109  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
110  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(),};
111 
112  // TODO (RC): This is an unattractive alternative to subclassing BlackboardArtifactNode,
113  // cut from the same cloth as the equally unattractive SHOW_UNIQUE_PATH array
114  // above. It should be removed when and if the subclassing is implemented.
115  private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
116  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),};
117 
118  private final PropertyChangeListener pcl = new PropertyChangeListener() {
119  @Override
120  public void propertyChange(PropertyChangeEvent evt) {
121  String eventType = evt.getPropertyName();
122  if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
124  if (event.getAddedTag().getArtifact().equals(artifact)) {
125  updateSheet();
126  }
127  } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
129  if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
130  updateSheet();
131  }
132  } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
134  if (event.getAddedTag().getContent().equals(associated)) {
135  updateSheet();
136  }
137  } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
139  if (event.getDeletedTagInfo().getContentID() == associated.getId()) {
140  updateSheet();
141  }
142  } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
144  if (event.getContentID() == associated.getId()) {
145  updateSheet();
146  }
147  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
148  if (evt.getNewValue() == null) {
149  // case was closed. Remove listeners so that we don't get called with a stale case handle
150  removeListeners();
151  contentCache.invalidateAll();
152  }
153  }
154  }
155  };
156 
165  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
166 
175  public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
176  super(artifact, createLookup(artifact));
177 
178  this.artifact = artifact;
179 
180  // Look for associated Content i.e. the source file for the artifact
181  for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
182  if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
183  this.associated = lookupContent;
184  break;
185  }
186  }
187 
188  this.setName(Long.toString(artifact.getArtifactID()));
189  this.setDisplayName();
190  this.setIconBaseWithExtension(iconPath);
192  }
193 
200  public BlackboardArtifactNode(BlackboardArtifact artifact) {
201  this(artifact, ExtractedContent.getIconFilePath(artifact.getArtifactTypeID()));
202  }
203 
213  @Override
214  protected void finalize() throws Throwable {
215  super.finalize();
216  removeListeners();
217  }
218 
219  private void removeListeners() {
221  }
222 
223  public BlackboardArtifact getArtifact() {
224  return this.artifact;
225  }
226 
227  @Override
228  @NbBundle.Messages({
229  "BlackboardArtifactNode.getAction.errorTitle=Error getting actions",
230  "BlackboardArtifactNode.getAction.resultErrorMessage=There was a problem getting actions for the selected result."
231  + " The 'View Result in Timeline' action will not be available.",
232  "BlackboardArtifactNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. "
233  + " The 'View File in Timeline' action will not be available."})
234  public Action[] getActions(boolean context) {
235  List<Action> actionsList = new ArrayList<>();
236  actionsList.addAll(Arrays.asList(super.getActions(context)));
237  AbstractFile file = getLookup().lookup(AbstractFile.class);
238 
239  //if this artifact has a time stamp add the action to view it in the timeline
240  try {
242  actionsList.add(new ViewArtifactInTimelineAction(artifact));
243  }
244  } catch (TskCoreException ex) {
245  logger.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
246  MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_resultErrorMessage());
247  }
248 
249  // if the artifact links to another file, add an action to go to that file
250  try {
251  AbstractFile c = findLinked(artifact);
252  if (c != null) {
254  }
255  } catch (TskCoreException ex) {
256  logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
257  MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_linkedFileMessage());
258  }
259 
260  //if the artifact has associated content, add the action to view the content in the timeline
261  if (null != file) {
263  }
264 
265  return actionsList.toArray(new Action[actionsList.size()]);
266  }
267 
268  @NbBundle.Messages({"# {0} - artifactDisplayName", "BlackboardArtifactNode.displayName.artifact={0} Artifact"})
274  private void setDisplayName() {
275  String displayName = ""; //NON-NLS
276 
277  // If this is a node for a keyword hit on an artifact, we set the
278  // display name to be the artifact type name followed by " Artifact"
279  // e.g. "Messages Artifact".
280  if (artifact != null
281  && (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
282  || artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID())) {
283  try {
284  for (BlackboardAttribute attribute : artifact.getAttributes()) {
285  if (attribute.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
286  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
287  if (associatedArtifact != null) {
288  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
289  artifact.getDisplayName();
290  } else {
291  displayName = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.displayName.artifact", associatedArtifact.getDisplayName());
292  }
293  }
294  }
295  }
296  } catch (TskCoreException | NoCurrentCaseException ex) {
297  // Do nothing since the display name will be set to the file name.
298  }
299  }
300 
301  if (displayName.isEmpty() && artifact != null) {
302  displayName = artifact.getName();
303  }
304 
305  this.setDisplayName(displayName);
306 
307  }
308 
314  public String getSourceName() {
315 
316  String srcName = "";
317  if (associated != null) {
318  srcName = associated.getName();
319  }
320  return srcName;
321  }
322 
323  @NbBundle.Messages({
324  "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
325  "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
326  "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
327  "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
328  "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
329  "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
330  "BlackboardArtifactNode.createSheet.fileSize.name=Size",
331  "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
332  "BlackboardArtifactNode.createSheet.path.displayName=Path",
333  "BlackboardArtifactNode.createSheet.path.name=Path"})
334 
335  @Override
336  protected Sheet createSheet() {
337  Sheet sheet = super.createSheet();
338  List<Tag> tags = getAllTagsFromDatabase();
339 
340  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
341  if (sheetSet == null) {
342  sheetSet = Sheet.createPropertiesSet();
343  sheet.put(sheetSet);
344  }
345 
346  Map<String, Object> map = new LinkedHashMap<>();
347  fillPropertyMap(map, artifact);
348 
349  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"),
350  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"),
351  NO_DESCR,
352  this.getSourceName()));
353 
354  addScoreProperty(sheetSet, tags);
355 
356  CorrelationAttributeInstance correlationAttribute = null;
358  correlationAttribute = getCorrelationAttributeInstance();
359  }
360  addCommentProperty(sheetSet, tags, correlationAttribute);
361 
363  addCountProperty(sheetSet, correlationAttribute);
364  }
365  if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
366  try {
367  BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
368  if (attribute != null) {
369  BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
370  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
371  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
372  NO_DESCR,
373  associatedArtifact.getDisplayName()));
374  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
375  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
376  NO_DESCR,
377  associatedArtifact.getShortDescription()));
378  }
379  } catch (TskCoreException | NoCurrentCaseException ex) {
380  // Do nothing since the display name will be set to the file name.
381  }
382  }
383 
384  for (Map.Entry<String, Object> entry : map.entrySet()) {
385  sheetSet.put(new NodeProperty<>(entry.getKey(),
386  entry.getKey(),
387  NO_DESCR,
388  entry.getValue()));
389  }
390 
391  //append custom node properties
392  if (customProperties != null) {
393  for (NodeProperty<? extends Object> np : customProperties) {
394  sheetSet.put(np);
395  }
396  }
397 
398  final int artifactTypeId = artifact.getArtifactTypeID();
399 
400  // If mismatch, add props for extension and file type
401  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
402  String ext = ""; //NON-NLS
403  String actualMimeType = ""; //NON-NLS
404  if (associated instanceof AbstractFile) {
405  AbstractFile af = (AbstractFile) associated;
406  ext = af.getNameExtension();
407  actualMimeType = af.getMIMEType();
408  if (actualMimeType == null) {
409  actualMimeType = ""; //NON-NLS
410  }
411  }
412  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
413  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
414  NO_DESCR,
415  ext));
416  sheetSet.put(new NodeProperty<>(
417  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
418  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
419  NO_DESCR,
420  actualMimeType));
421  }
422 
423  if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
424  String sourcePath = ""; //NON-NLS
425  try {
426  sourcePath = associated.getUniquePath();
427  } catch (TskCoreException ex) {
428  logger.log(Level.WARNING, "Failed to get unique path from: {0}", associated.getName()); //NON-NLS
429  }
430 
431  if (sourcePath.isEmpty() == false) {
432  sheetSet.put(new NodeProperty<>(
433  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
434  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
435  NO_DESCR,
436  sourcePath));
437  }
438 
439  if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
440  AbstractFile file = associated instanceof AbstractFile ? (AbstractFile) associated : null;
441  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
442  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
443  "",
444  file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file)));
445  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
446  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
447  "",
448  file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file)));
449  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
450  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
451  "",
452  file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file)));
453  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
454  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
455  "",
456  file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file)));
457  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
458  NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
459  "",
460  associated.getSize()));
461  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
462  Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
463  "",
464  file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
465  }
466  } else {
467  String dataSourceStr = "";
468  try {
469  Content dataSource = associated.getDataSource();
470  if (dataSource != null) {
471  dataSourceStr = dataSource.getName();
472  } else {
473  dataSourceStr = getRootParentName();
474  }
475  } catch (TskCoreException ex) {
476  logger.log(Level.WARNING, "Failed to get image name from {0}", associated.getName()); //NON-NLS
477  }
478 
479  if (dataSourceStr.isEmpty() == false) {
480  sheetSet.put(new NodeProperty<>(
481  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
482  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
483  NO_DESCR,
484  dataSourceStr));
485  }
486  }
487 
488  // If EXIF, add props for file size and path
489  if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
490 
491  long size = 0;
492  String path = ""; //NON-NLS
493  if (associated instanceof AbstractFile) {
494  AbstractFile af = (AbstractFile) associated;
495  size = af.getSize();
496  try {
497  path = af.getUniquePath();
498  } catch (TskCoreException ex) {
499  path = af.getParentPath();
500  }
501  }
502  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
503  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
504  NO_DESCR,
505  size));
506  sheetSet.put(new NodeProperty<>(
507  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
508  NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
509  NO_DESCR,
510  path));
511  }
512 
513  return sheet;
514  }
515 
523  protected final List<Tag> getAllTagsFromDatabase() {
524  List<Tag> tags = new ArrayList<>();
525  try {
528  } catch (TskCoreException | NoCurrentCaseException ex) {
529  logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
530  }
531  return tags;
532  }
533 
541  @NbBundle.Messages({
542  "BlackboardArtifactNode.createSheet.tags.displayName=Tags"})
543  @Deprecated
544  protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
545  // add properties for tags
546  List<Tag> tags = new ArrayList<>();
547  try {
550  } catch (TskCoreException | NoCurrentCaseException ex) {
551  logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
552  }
553  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(),
554  NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
555  }
556 
566  @Deprecated
567  protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
568  sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(),
569  NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
570  }
571 
573  CorrelationAttributeInstance correlationAttribute = null;
574  if (EamDb.isEnabled()) {
575  correlationAttribute = EamArtifactUtil.getInstanceFromContent(associated);
576  }
577  return correlationAttribute;
578  }
579 
591  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
592  "BlackboardArtifactNode.createSheet.comment.displayName=C"})
593  protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
595  for (Tag tag : tags) {
596  if (!StringUtils.isBlank(tag.getComment())) {
597  //if the tag is null or empty or contains just white space it will indicate there is not a comment
598  status = HasCommentStatus.TAG_COMMENT;
599  break;
600  }
601  }
602  //currently checks for a comment on the associated file in the central repo not the artifact itself
603  //what we want the column property to reflect should be revisted when we have added a way to comment
604  //on the artifact itself
605  if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
606  if (status == HasCommentStatus.TAG_COMMENT) {
608  } else {
609  status = HasCommentStatus.CR_COMMENT;
610  }
611  }
612  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
613  status));
614  }
615 
624  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
625  "BlackboardArtifactNode.createSheet.score.displayName=S",
626  "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
627  "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
628  "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
629  "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
630  "BlackboardArtifactNode.createSheet.noScore.description=No score"})
631  protected final void addScoreProperty(Sheet.Set sheetSet, List<Tag> tags) {
632  Score score = Score.NO_SCORE;
633  String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
634  if (associated instanceof AbstractFile) {
635  if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) {
636  score = Score.NOTABLE_SCORE;
637  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
638  }
639  }
640  //if the artifact being viewed is a hashhit check if the hashset is notable
641  if ((score == Score.NO_SCORE || score == Score.INTERESTING_SCORE) && content.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
642  try {
643  BlackboardAttribute attr = content.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
645  for (HashDbManager.HashDb hashDb : notableHashsets) {
646  if (hashDb.getHashSetName().equals(attr.getValueString())) {
647  score = Score.NOTABLE_SCORE;
648  description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
649  break;
650  }
651  }
652  } catch (TskCoreException ex) {
653  //unable to get the attribute so we can not update the status based on the attribute
654  logger.log(Level.WARNING, "Unable to get TSK_SET_NAME attribute for artifact of type TSK_HASHSET_HIT with artifact ID " + content.getArtifactID(), ex);
655  }
656  }
657  try {
658  if (score == Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
659  score = Score.INTERESTING_SCORE;
660  description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description();
661  }
662  } catch (TskCoreException ex) {
663  logger.log(Level.WARNING, "Error getting artifacts for artifact: " + content.getName(), ex);
664  }
665  if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
666  score = Score.INTERESTING_SCORE;
667  description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
668  for (Tag tag : tags) {
669  if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
670  score = Score.NOTABLE_SCORE;
671  description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description();
672  break;
673  }
674  }
675  }
676  sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score));
677  }
678 
679  @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
680  "BlackboardArtifactNode.createSheet.count.displayName=O",
681  "BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
682  "BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated",
683  "# {0} - occuranceCount",
684  "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
685 
686  protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
687  Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
688  String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
689  try {
690  //don't perform the query if there is no correlation value
691  if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
693  description = Bundle.BlackboardArtifactNode_createSheet_count_description(count);
694  } else if (attribute != null) {
695  description = Bundle.BlackboardArtifactNode_createSheet_count_hashLookupNotRun_description();
696  }
697  } catch (EamDbException ex) {
698  logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
700  logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
701  }
702  sheetSet.put(
703  new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count));
704  }
705 
706  private void updateSheet() {
707  this.setSheet(createSheet());
708  }
709 
710  private String getRootParentName() {
711  String parentName = associated.getName();
712  Content parent = associated;
713  try {
714  while ((parent = parent.getParent()) != null) {
715  parentName = parent.getName();
716  }
717  } catch (TskCoreException ex) {
718  logger.log(Level.WARNING, "Failed to get parent name from {0}", associated.getName()); //NON-NLS
719  return "";
720  }
721  return parentName;
722  }
723 
731  if (null == customProperties) {
732  //lazy create the list
733  customProperties = new ArrayList<>();
734  }
735  customProperties.add(np);
736  }
737 
745  @SuppressWarnings("deprecation")
746  private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
747  try {
748  for (BlackboardAttribute attribute : artifact.getAttributes()) {
749  final int attributeTypeID = attribute.getAttributeType().getTypeID();
750  //skip some internal attributes that user shouldn't see
751  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
752  || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
753  || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
754  || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
755  || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()) {
756  } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
757  addEmailMsgProperty(map, attribute);
758  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
759  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
760  } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
761  && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
762  /*
763  * This was added because the RegRipper output would often
764  * cause the UI to get a black line accross it and hang if
765  * you hovered over large output or selected it. This
766  * reduces the amount of data in the table. Could consider
767  * doing this for all fields in the UI.
768  */
769  String value = attribute.getDisplayString();
770  if (value.length() > 512) {
771  value = value.substring(0, 512);
772  }
773  map.put(attribute.getAttributeType().getDisplayName(), value);
774  } else {
775  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
776  }
777  }
778  } catch (TskCoreException ex) {
779  logger.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS
780  }
781  }
782 
790  private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
791 
792  final int attributeTypeID = attribute.getAttributeType().getTypeID();
793 
794  // Skip certain Email msg attributes
795  if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
796  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
797  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
798  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
799  || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
800  || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
801 
802  // do nothing
803  } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
804 
805  String value = attribute.getDisplayString();
806  if (value.length() > 160) {
807  value = value.substring(0, 160) + "...";
808  }
809  map.put(attribute.getAttributeType().getDisplayName(), value);
810  } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
811  map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
812  } else {
813  map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
814  }
815 
816  }
817 
818  @Override
819  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
820  return visitor.visit(this);
821  }
822 
831  private static Lookup createLookup(BlackboardArtifact artifact) {
832  // Add the content the artifact is associated with
833  final long objectID = artifact.getObjectID();
834  try {
835  Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
836  if (content == null) {
837  return Lookups.fixed(artifact);
838  } else {
839  return Lookups.fixed(artifact, content);
840  }
841  } catch (ExecutionException ex) {
842  logger.log(Level.WARNING, "Getting associated content for artifact failed", ex); //NON-NLS
843  return Lookups.fixed(artifact);
844  }
845  }
846 
847  @Override
848  public boolean isLeafTypeNode() {
849  return true;
850  }
851 
852  @Override
853  public String getItemType() {
854  return getClass().getName();
855  }
856 
857  @Override
858  public <T> T accept(ContentNodeVisitor<T> visitor) {
859  return visitor.visit(this);
860  }
861 }
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)
final void addScoreProperty(Sheet.Set sheetSet, List< Tag > tags)
static ViewFileInTimelineAction createViewSourceFileAction(AbstractFile file)
Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttributeInstance.Type aType, String value)
static CorrelationAttributeInstance getInstanceFromContent(Content content)
void addEmailMsgProperty(Map< String, Object > map, BlackboardAttribute attribute)
final void addCommentProperty(Sheet.Set sheetSet, List< Tag > tags, CorrelationAttributeInstance attribute)
final CorrelationAttributeInstance getCorrelationAttributeInstance()
final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:437
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:482
static ViewFileInTimelineAction createViewFileAction(AbstractFile file)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.