Autopsy 4.23.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
BlackboardArtifactNode.java
Go to the documentation of this file.
1/*
2 * Autopsy
3 *
4 * Copyright 2012-2026 Sleuth Kit Labs
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 */
19package org.sleuthkit.autopsy.datamodel;
20
21import org.sleuthkit.autopsy.actions.ViewArtifactAction;
22import org.sleuthkit.autopsy.actions.ViewOsAccountAction;
23import com.google.common.annotations.Beta;
24import com.google.common.cache.Cache;
25import com.google.common.cache.CacheBuilder;
26import java.beans.PropertyChangeEvent;
27import java.beans.PropertyChangeListener;
28import java.lang.ref.WeakReference;
29import java.text.MessageFormat;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Collections;
33import java.util.EnumSet;
34import java.util.LinkedHashMap;
35import java.util.List;
36import java.util.Map;
37import java.util.MissingResourceException;
38import java.util.Optional;
39import java.util.Set;
40import java.util.concurrent.ExecutionException;
41import java.util.concurrent.TimeUnit;
42import java.util.logging.Level;
43import java.util.stream.Collectors;
44import java.util.stream.Stream;
45import javax.swing.Action;
46import javax.swing.SwingUtilities;
47import org.apache.commons.lang3.StringUtils;
48import org.apache.commons.lang3.tuple.Pair;
49import org.openide.nodes.Node;
50import org.openide.nodes.Sheet;
51import org.openide.util.Lookup;
52import org.openide.util.NbBundle;
53import org.openide.util.NbBundle.Messages;
54import org.openide.util.Utilities;
55import org.openide.util.WeakListeners;
56import org.openide.util.lookup.Lookups;
57import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
58import org.sleuthkit.autopsy.actions.AddContentTagAction;
59import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
60import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
61import org.sleuthkit.autopsy.casemodule.Case;
62import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
63import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
64import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
65import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
66import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
67import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
68import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
69import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
70import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
71import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
72import org.sleuthkit.autopsy.core.UserPreferences;
73import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
74import org.sleuthkit.autopsy.coreutils.Logger;
75import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
76import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
77import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
78import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
79import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
80import org.sleuthkit.datamodel.AbstractFile;
81import org.sleuthkit.datamodel.BlackboardArtifact;
82import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
83import org.sleuthkit.datamodel.BlackboardAttribute;
84import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
85import org.sleuthkit.datamodel.Content;
86import org.sleuthkit.datamodel.Tag;
87import org.sleuthkit.datamodel.TskCoreException;
88import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
89import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
90import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
91import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
92import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
93import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
94import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
95import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
96import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
97import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
98import org.sleuthkit.autopsy.directorytree.ExtractAction;
99import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
100import org.sleuthkit.autopsy.directorytree.ViewContextAction;
101import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
102import org.sleuthkit.datamodel.AnalysisResult;
103import org.sleuthkit.datamodel.BlackboardArtifact.Category;
104import org.sleuthkit.datamodel.HostAddress;
105import org.sleuthkit.datamodel.Pool;
106import org.sleuthkit.datamodel.DataArtifact;
107import org.sleuthkit.datamodel.DerivedFile;
108import org.sleuthkit.datamodel.Directory;
109import org.sleuthkit.datamodel.File;
110import org.sleuthkit.datamodel.LayoutFile;
111import org.sleuthkit.datamodel.LocalDirectory;
112import org.sleuthkit.datamodel.LocalFile;
113import org.sleuthkit.datamodel.OsAccount;
114import org.sleuthkit.datamodel.Report;
115import org.sleuthkit.datamodel.Score;
116import org.sleuthkit.datamodel.SlackFile;
117import org.sleuthkit.datamodel.VirtualDirectory;
118import org.sleuthkit.datamodel.TskData;
119import org.sleuthkit.datamodel.Volume;
120import org.sleuthkit.datamodel.VolumeSystem;
121import org.sleuthkit.datamodel.Image;
122
127public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
128
129 private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
130
131 /*
132 * Cache of Content objects used to avoid repeated trips to the case
133 * database to retrieve Content objects that are the source of multiple
134 * artifacts.
135 */
136 private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
137
138 /*
139 * Case events that indicate an update to the node's property sheet may be
140 * required.
141 */
142 private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
149
150 /*
151 * Artifact types for which the file metadata of the artifact's source file
152 * should be displayed in the node's property sheet.
153 *
154 * @SuppressWarnings("deprecation") - we need to support already existing
155 * interesting file and artifact hits.
156 */
157 @SuppressWarnings("deprecation")
158 private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
159 BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
160 BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID()
161 };
162
163 private static final java.util.Collection<? extends ArtifactPropertyEnricher> ENRICHERS
164 = Lookup.getDefault().lookupAll(ArtifactPropertyEnricher.class);
165
166 private final BlackboardArtifact artifact;
167 private final BlackboardArtifact.Type artifactType;
168 private Content srcContent;
169 private volatile String translatedSourceName;
170 private final String sourceObjTypeName;
171
172 /*
173 * A method has been provided to allow the injection of properties into this
174 * node for display in the node's property sheet, independent of the
175 * artifact the node represents.
176 */
177 private List<NodeProperty<? extends Object>> customProperties;
178
179 private final PropertyChangeListener listener = new PropertyChangeListener() {
180 @Override
181 public void propertyChange(PropertyChangeEvent evt) {
182 String eventType = evt.getPropertyName();
183 if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())) {
185 if (event.getAddedTag().getArtifact().equals(artifact)) {
186 updateSheet();
187 }
188 } else if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())) {
190 if (event.getDeletedTagInfo().getArtifactID() == artifact.getArtifactID()) {
191 updateSheet();
192 }
193 } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
195 if (event.getAddedTag().getContent().equals(srcContent)) {
196 updateSheet();
197 }
198 } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
200 if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
201 updateSheet();
202 }
203 } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
205 if (event.getContentID() == srcContent.getId()) {
206 updateSheet();
207 }
208 } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
209 if (evt.getNewValue() == null) {
210 /*
211 * The case has been closed.
212 */
214 contentCache.invalidateAll();
215 }
216 } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
217 updateSCOColumns((SCOData) evt.getNewValue());
218 } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
219 /*
220 * Replace the value of the Source File property with the
221 * translated name via setDisplayName (see note in createSheet),
222 * and put the untranslated name in the Original Name property
223 * and in the tooltip.
224 */
225 String originalName = evt.getOldValue().toString();
226 translatedSourceName = evt.getNewValue().toString();
227 setDisplayName(translatedSourceName);
228 setShortDescription(originalName);
230 Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
231 Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
232 NO_DESCR,
233 originalName));
234 }
235 }
236 };
237
238 /*
239 * The node's event listener is wrapped in a weak reference that allows the
240 * node to be garbage collected when the NetBeans infrastructure discards
241 * it. If this is not done, it has been shown that strong references to the
242 * listener held by event publishers prevents garbage collection of this
243 * node.
244 */
245 private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
246
257 public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
258 super(artifact, createLookup(artifact, false));
259 this.artifact = artifact;
260 this.artifactType = getType(artifact);
261
263
264 if (srcContent == null) {
265 throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
266 }
267
268 try {
269 /*
270 * Calling this getter causes the unique path of the source content
271 * to be cached in the Content object. This is advantageous as long
272 * as this node is constructed in a background thread instead of a
273 * UI thread.
274 */
275 srcContent.getUniquePath();
276 } catch (TskCoreException ex) {
277 logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
278 }
281 setName(Long.toString(artifact.getArtifactID()));
282 setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
284 }
285
297 @Beta
298 public BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup) {
299 super(artifact, createLookup(artifact, useAssociatedFileInLookup));
300 this.artifact = artifact;
301 this.artifactType = getType(artifact);
302
303 try {
304 srcContent = artifact.getParent();
305 } catch (TskCoreException ex) {
306 logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
307 }
308
309 if (srcContent != null) {
310 try {
311 /*
312 * Calling this getter causes the unique path of the source
313 * content to be cached in the Content object. This is
314 * advantageous as long as this node is constructed in a
315 * background thread instead of a UI thread.
316 */
317 srcContent.getUniquePath();
318 } catch (TskCoreException ex) {
319 logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
320 }
321 } else {
322 throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
323 }
325 setName(Long.toString(artifact.getArtifactID()));
327 String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
328 setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
330 }
331
340 public BlackboardArtifactNode(BlackboardArtifact artifact) {
341 this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
342 }
343
351 private static BlackboardArtifact.Type getType(BlackboardArtifact artifact) {
352 try {
353 return artifact.getType();
354 } catch (TskCoreException ex) {
355 logger.log(Level.WARNING, MessageFormat.format("Error getting the artifact type for artifact (artifact objID={0})", artifact.getId()), ex);
356 return null;
357 }
358 }
359
371 private static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile) {
372 /*
373 * Get the source content.
374 */
375 Content content = null;
376 try {
377 if (useAssociatedFile) {
379 } else {
380 long srcObjectID = artifact.getObjectID();
381 content = contentCache.get(srcObjectID, () -> artifact.getSleuthkitCase().getContentById(srcObjectID));
382 }
383 } catch (ExecutionException ex) {
384 logger.log(Level.SEVERE, MessageFormat.format("Error getting source/associated content (artifact object ID={0})", artifact.getId()), ex); //NON-NLS
385 }
386
387 /*
388 * Make an Autopsy Data Model wrapper for the artifact.
389 *
390 * NOTE: The creation of an Autopsy Data Model independent of the
391 * NetBeans nodes is a work in progress. At the time this comment is
392 * being written, this object is only being used to indicate the item
393 * represented by this BlackboardArtifactNode.
394 */
395 BlackboardArtifactItem<?> artifactItem;
396 if (artifact instanceof AnalysisResult) {
397 artifactItem = new AnalysisResultItem((AnalysisResult) artifact, content);
398 } else {
399 artifactItem = new DataArtifactItem((DataArtifact) artifact, content);
400 }
401
402 /*
403 * Create the Lookup.
404 *
405 * NOTE: For now, we are putting both the Autopsy Data Model item and
406 * the Sleuth Kit Data Model item in the Lookup so that code that is not
407 * aware of the new Autopsy Data Model will still function.
408 */
409 if (content == null) {
410 return Lookups.fixed(artifact, artifactItem);
411 } else {
412 return Lookups.fixed(artifact, artifactItem, content);
413 }
414 }
415
424 private Content getSourceContentFromLookup(BlackboardArtifact artifact) {
425 for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
426 /*
427 * NOTE: createLookup() saves the artifact and its source content
428 * (if one exists). However, createLookup() has to be static because
429 * it is being called by super(), therefore it can't store the
430 * source content in this.srcContent class variable. That's why we
431 * have to have the logic below, which reads the Lookup contents,
432 * and decides that the source content is the entry in Lookup that
433 * is NOT the input artifact.
434 */
435 if ((lookupContent != null) && (lookupContent.getId() != artifact.getId())) {
436 return lookupContent;
437 }
438 }
439 return null;
440 }
441
455 private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
456 try {
457 BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
458 if (attribute != null) {
459 return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
460 }
461 } catch (TskCoreException ex) {
462 logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
463 }
464 return null;
465 }
466
476 @Override
477 protected void finalize() throws Throwable {
478 super.finalize();
480 }
481
488
494 public BlackboardArtifact getArtifact() {
495 return this.artifact;
496 }
497
505 private List<Action> getNonNull(Action... items) {
506 return Stream.of(items)
507 .filter(i -> i != null)
508 .collect(Collectors.toList());
509 }
510
511 @Override
512 public Action[] getActions(boolean context) {
513 // groupings of actions where each group will be separated by a divider
514 List<List<Action>> actionsLists = new ArrayList<>();
515
516 // view artifact in timeline
517 actionsLists.add(getNonNull(
518 getTimelineArtifactAction(this.artifact)
519 ));
520
521 // view associated file (TSK_PATH_ID attr) in directory and timeline
522 actionsLists.add(getAssociatedFileActions(this.artifact, this.artifactType));
523
524 // view source content in directory and timeline
525 actionsLists.add(getNonNull(
526 getViewSrcContentAction(this.artifact, this.srcContent),
527 getTimelineSrcContentAction(this.srcContent)
528 ));
529
530 // extract with password from encrypted file
531 actionsLists.add(getNonNull(
532 getExtractWithPasswordAction(this.srcContent)
533 ));
534
535 // menu options for artifact with report parent
536 if (this.srcContent instanceof Report) {
537 actionsLists.add(DataModelActionsFactory.getActions(this.srcContent, false));
538 }
539
540 Node parentFileNode = getParentFileNode(srcContent);
541 int selectedFileCount = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).size();
542 int selectedArtifactCount = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class).size();
543
544 // view source content if source content is some sort of file
545 actionsLists.add(getSrcContentViewerActions(parentFileNode, selectedFileCount));
546
547 // extract / export if source content is some sort of file
548 if (parentFileNode != null) {
549 actionsLists.add(Arrays.asList(ExtractAction.getInstance(), ExportCSVAction.getInstance()));
550 }
551
552 // file and result tagging
553 actionsLists.add(getTagActions(parentFileNode != null, this.artifact, selectedFileCount, selectedArtifactCount));
554
555 // menu extension items (i.e. add to central repository)
556 actionsLists.add(ContextMenuExtensionPoint.getActions());
557
558 // netbeans default items (i.e. properties)
559 actionsLists.add(Arrays.asList(super.getActions(context)));
560
561 return actionsLists.stream()
562 // remove any empty lists
563 .filter((lst) -> lst != null && !lst.isEmpty())
564 // add in null between each list group
565 .flatMap(lst -> Stream.concat(Stream.of((Action) null), lst.stream()))
566 // skip the first null
567 .skip(1)
568 .toArray(sz -> new Action[sz]);
569 }
570
579 @Messages({
580 "BlackboardArtifactNode_getAssociatedTypeStr_webCache=Cached File",
581 "BlackboardArtifactNode_getAssociatedTypeStr_webDownload=Downloaded File",
582 "BlackboardArtifactNode_getAssociatedTypeStr_associated=Associated File",})
583 private String getAssociatedTypeStr(BlackboardArtifact.Type artifactType) {
584 if (BlackboardArtifact.Type.TSK_WEB_CACHE.equals(artifactType)) {
585 return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webCache();
586 } else if (BlackboardArtifact.Type.TSK_WEB_DOWNLOAD.equals(artifactType)) {
587 return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webDownload();
588 } else {
589 return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_associated();
590 }
591 }
592
601 @Messages({
602 "BlackboardArtifactNode_getViewSrcContentAction_type_File=File",
603 "BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact=Data Artifact",
604 "BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount=OS Account",
605 "BlackboardArtifactNode_getViewSrcContentAction_type_unknown=Item"
606 })
607 private String getContentTypeStr(Content content) {
608 if (content instanceof AbstractFile) {
609 return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_File();
610 } else if (content instanceof DataArtifact) {
611 return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact();
612 } else if (content instanceof OsAccount) {
613 return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount();
614 } else {
615 return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_unknown();
616 }
617 }
618
629 @Messages({
630 "# {0} - type",
631 "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction=View {0} in Directory",
632 "# {0} - type",
633 "BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction=View {0} in Timeline..."
634 })
635 private List<Action> getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType) {
636 try {
637 AbstractFile associatedFile = findLinked(artifact);
638 if (associatedFile != null) {
639 return Arrays.asList(
641 Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction(
643 associatedFile),
644 new ViewFileInTimelineAction(associatedFile,
645 Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction(
647 );
648 }
649
650 } catch (TskCoreException ex) {
651 logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
652 }
653 return Collections.emptyList();
654 }
655
664 @Messages({
665 "# {0} - contentType",
666 "BlackboardArtifactNode_getSrcContentAction_actionDisplayName=View Source {0} in Directory"
667 })
668 private Action getViewSrcContentAction(BlackboardArtifact artifact, Content content) {
669 if (content instanceof DataArtifact) {
670 return new ViewArtifactAction(
671 (BlackboardArtifact) content,
672 Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
674 } else if (content instanceof OsAccount) {
675 return new ViewOsAccountAction(
676 (OsAccount) content,
677 Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
679 } else if (content instanceof AbstractFile || artifact instanceof DataArtifact) {
680 return new ViewContextAction(
681 Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
683 content);
684 } else {
685 return null;
686 }
687 }
688
697 private Node getParentFileNode(Content content) {
698 if (content instanceof File) {
699 return new FileNode((AbstractFile) content);
700 } else if (content instanceof Directory) {
701 return new DirectoryNode((Directory) content);
702 } else if (content instanceof VirtualDirectory) {
703 return new VirtualDirectoryNode((VirtualDirectory) content);
704 } else if (content instanceof LocalDirectory) {
705 return new LocalDirectoryNode((LocalDirectory) content);
706 } else if (content instanceof LayoutFile) {
707 return new LayoutFileNode((LayoutFile) content);
708 } else if (content instanceof LocalFile || content instanceof DerivedFile) {
709 return new LocalFileNode((AbstractFile) content);
710 } else if (content instanceof SlackFile) {
711 return new SlackFileNode((AbstractFile) content);
712 } else {
713 return null;
714 }
715 }
716
724 private Action getExtractWithPasswordAction(Content srcContent) {
725 if ((srcContent instanceof AbstractFile)
727 .contains("." + ((AbstractFile) srcContent).getNameExtension().toLowerCase())) {
728 try {
729 if (srcContent.getArtifacts(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED.getTypeID()).size() > 0) {
730 return new ExtractArchiveWithPasswordAction((AbstractFile) srcContent);
731 }
732 } catch (TskCoreException ex) {
733 logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
734 }
735 }
736
737 return null;
738 }
739
751 private List<Action> getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount) {
752 List<Action> actionsList = new ArrayList<>();
753
754 // don't show AddContentTagAction for data artifacts.
755 if (hasSrcFile && !(artifact instanceof DataArtifact)) {
756 actionsList.add(AddContentTagAction.getInstance());
757 }
758
760
761 // don't show DeleteFileContentTagAction for data artifacts.
762 if (hasSrcFile && (!(artifact instanceof DataArtifact)) && (selectedFileCount == 1)) {
763 actionsList.add(DeleteFileContentTagAction.getInstance());
764 }
765
766 if (selectedArtifactCount == 1) {
768 }
769
770 return actionsList;
771 }
772
781 @Messages({
782 "BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin=View Item in New Window",
783 "BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer=Open in External Viewer Ctrl+E"
784 })
785 private List<Action> getSrcContentViewerActions(Node srcFileNode, int selectedFileCount) {
786 List<Action> actionsList = new ArrayList<>();
787 if (srcFileNode != null) {
788 actionsList.add(new NewWindowViewAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin(), srcFileNode));
789 if (selectedFileCount == 1) {
790 actionsList.add(new ExternalViewerAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer(), srcFileNode));
791 } else {
792 actionsList.add(ExternalViewerShortcutAction.getInstance());
793 }
794 }
795 return actionsList;
796 }
797
806 @NbBundle.Messages({
807 "# {0} - contentType",
808 "BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName=View Source {0} in Timeline... "
809 })
810 private Action getTimelineSrcContentAction(Content srcContent) {
811 if (srcContent instanceof AbstractFile) {
812 return new ViewFileInTimelineAction((AbstractFile) srcContent,
813 Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
815 } else if (srcContent instanceof DataArtifact) {
816 try {
818 return new ViewArtifactInTimelineAction((BlackboardArtifact) srcContent,
819 Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
821 }
822 } catch (TskCoreException ex) {
823 logger.log(Level.SEVERE, MessageFormat.format("Error getting source data artifact timestamp (artifact objID={0})", srcContent.getId()), ex); //NON-NLS
824 }
825 }
826
827 return null;
828 }
829
838 @Messages({
839 "BlackboardArtifactNode_getTimelineArtifactAction_displayName=View Selected Item in Timeline... "
840 })
841 private Action getTimelineArtifactAction(BlackboardArtifact art) {
842 try {
843 // don't show ViewArtifactInTimelineAction for AnalysisResults.
844 if (!(art instanceof AnalysisResult) && ViewArtifactInTimelineAction.hasSupportedTimeStamp(art)) {
845 return new ViewArtifactInTimelineAction(art, Bundle.BlackboardArtifactNode_getTimelineArtifactAction_displayName());
846 }
847 } catch (TskCoreException ex) {
848 logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", art.getId()), ex); //NON-NLS
849 }
850
851 return null;
852 }
853
860 public String getSourceName() {
861 return srcContent.getName();
862 }
863
864 @NbBundle.Messages({
865 "BlackboardArtifactNode.createSheet.srcFile.name=Source Name",
866 "BlackboardArtifactNode.createSheet.srcFile.displayName=Source Name",
867 "BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
868 "BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
869 "BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
870 "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
871 "BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
872 "BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details",
873 "BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
874 "BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
875 "BlackboardArtifactNode.createSheet.fileSize.name=Size",
876 "BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
877 "BlackboardArtifactNode.createSheet.path.displayName=Path",
878 "BlackboardArtifactNode.createSheet.path.name=Path"
879 })
880 /*
881 * @SuppressWarnings("deprecation") - we need to support already existing
882 * interesting file and artifact hits.
883 */
884 @SuppressWarnings("deprecation")
885 @Override
886 protected Sheet createSheet() {
887 /*
888 * Create an empty property sheet.
889 */
890 Sheet sheet = super.createSheet();
891 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
892 if (sheetSet == null) {
893 sheetSet = Sheet.createPropertiesSet();
894 sheet.put(sheetSet);
895 }
896
897 /*
898 * Add the name of the source content of the artifact represented by
899 * this node to the sheet. The value of this property is the same as the
900 * display name of the node and this a "special" property that displays
901 * the node's icon as well as the display name.
902 */
903 sheetSet.put(new NodeProperty<>(
904 Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
905 Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
906 NO_DESCR,
907 getDisplayName()));
908
909 GetSCOTask scoTask;
910 if (artifact instanceof AnalysisResult
911 && !(artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
912 || artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())) {
913 scoTask = updateSheetForAnalysisResult((AnalysisResult) artifact, sheetSet);
914 } else {
915 scoTask = addSCOColumns(sheetSet);
916 }
917
919 /*
920 * If machine translation is configured, add the original name of
921 * the of the source content of the artifact represented by this
922 * node to the sheet.
923 */
924 sheetSet.put(new NodeProperty<>(
925 Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
926 Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
927 NO_DESCR,
928 translatedSourceName != null ? srcContent.getName() : ""));
929 if (translatedSourceName == null) {
930 /*
931 * NOTE: The task makes its own weak reference to the listener.
932 */
933 new FileNameTransTask(srcContent.getName(), this, listener).submit();
934 }
935 }
936
937 /*
938 * If the artifact represented by this node is an interesting artifact
939 * hit, add the type and description of the interesting artifact to the
940 * sheet.
941 */
942 if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() || artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID()) {
943 try {
944 BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
945 if (attribute != null) {
946 BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
947 sheetSet.put(new NodeProperty<>(
948 NbBundle.getMessage(BlackboardArtifactNode.class,
949 "BlackboardArtifactNode.createSheet.artifactType.name"),
950 NbBundle.getMessage(BlackboardArtifactNode.class,
951 "BlackboardArtifactNode.createSheet.artifactType.displayName"),
952 NO_DESCR,
953 associatedArtifact.getDisplayName()));
954 sheetSet.put(new NodeProperty<>(
955 NbBundle.getMessage(BlackboardArtifactNode.class,
956 "BlackboardArtifactNode.createSheet.artifactDetails.name"),
957 NbBundle.getMessage(BlackboardArtifactNode.class,
958 "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
959 NO_DESCR,
960 associatedArtifact.getShortDescription()));
961 }
962 } catch (TskCoreException | NoCurrentCaseException ex) {
963 logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact with type " + artifact.getArtifactTypeName() + " artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
964 }
965 }
966
967 /*
968 * Add the attributes of the artifact represented by this node to the
969 * sheet.
970 */
971 Map<String, Object> map = new LinkedHashMap<>();
973 for (Map.Entry<String, Object> entry : map.entrySet()) {
974 sheetSet.put(new NodeProperty<>(entry.getKey(),
975 entry.getKey(),
976 NO_DESCR,
977 entry.getValue()));
978 }
979
980 /*
981 * Add any "custom properties" for the node to the sheet.
982 */
983 if (customProperties != null) {
985 sheetSet.put(np);
986 }
987 }
988
989 /*
990 * If the artifact represented by this node is a file extension mismatch
991 * artifact, add the extension and type of the artifact's source file to
992 * the sheet.
993 */
994 final int artifactTypeId = artifact.getArtifactTypeID();
995 if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
996 String ext = ""; //NON-NLS
997 String actualMimeType = ""; //NON-NLS
998 if (srcContent instanceof AbstractFile) {
999 AbstractFile file = (AbstractFile) srcContent;
1000 ext = file.getNameExtension();
1001 actualMimeType = file.getMIMEType();
1002 if (actualMimeType == null) {
1003 actualMimeType = ""; //NON-NLS
1004
1005 }
1006 }
1007 sheetSet.put(new NodeProperty<>(
1008 NbBundle.getMessage(BlackboardArtifactNode.class,
1009 "BlackboardArtifactNode.createSheet.ext.name"),
1010 NbBundle.getMessage(BlackboardArtifactNode.class,
1011 "BlackboardArtifactNode.createSheet.ext.displayName"),
1012 NO_DESCR,
1013 ext));
1014 sheetSet.put(new NodeProperty<>(
1015 NbBundle.getMessage(BlackboardArtifactNode.class,
1016 "BlackboardArtifactNode.createSheet.mimeType.name"),
1017 NbBundle.getMessage(BlackboardArtifactNode.class,
1018 "BlackboardArtifactNode.createSheet.mimeType.displayName"),
1019 NO_DESCR,
1020 actualMimeType));
1021 }
1022
1023 /*
1024 * If the type of the artifact represented by this node dictates the
1025 * addition of the source content's unique path, add it to the sheet.
1026 */
1027 if (artifactType != null && artifactType.getCategory() == Category.ANALYSIS_RESULT) {
1028 String sourcePath = ""; //NON-NLS
1029 try {
1030 sourcePath = srcContent.getUniquePath();
1031 } catch (TskCoreException ex) {
1032 logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1033
1034 }
1035
1036 if (sourcePath.isEmpty() == false) {
1037 sheetSet.put(new NodeProperty<>(
1038 NbBundle.getMessage(BlackboardArtifactNode.class,
1039 "BlackboardArtifactNode.createSheet.filePath.name"),
1040 NbBundle.getMessage(BlackboardArtifactNode.class,
1041 "BlackboardArtifactNode.createSheet.filePath.displayName"),
1042 NO_DESCR,
1043 sourcePath));
1044 }
1045
1046 /*
1047 * If the type of the artifact represented by this node dictates the
1048 * addition of the source content's file metadata, add it to the
1049 * sheet. Otherwise, add the data source to the sheet.
1050 */
1051 if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
1052 AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
1053 sheetSet.put(new NodeProperty<>(
1054 NbBundle.getMessage(BlackboardArtifactNode.class,
1055 "ContentTagNode.createSheet.fileModifiedTime.name"),
1056 NbBundle.getMessage(BlackboardArtifactNode.class,
1057 "ContentTagNode.createSheet.fileModifiedTime.displayName"),
1058 "",
1059 file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
1060 sheetSet.put(new NodeProperty<>(
1061 NbBundle.getMessage(BlackboardArtifactNode.class,
1062 "ContentTagNode.createSheet.fileChangedTime.name"),
1063 NbBundle.getMessage(BlackboardArtifactNode.class,
1064 "ContentTagNode.createSheet.fileChangedTime.displayName"),
1065 "",
1066 file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
1067 sheetSet.put(new NodeProperty<>(
1068 NbBundle.getMessage(BlackboardArtifactNode.class,
1069 "ContentTagNode.createSheet.fileAccessedTime.name"),
1070 NbBundle.getMessage(BlackboardArtifactNode.class,
1071 "ContentTagNode.createSheet.fileAccessedTime.displayName"),
1072 "",
1073 file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
1074 sheetSet.put(new NodeProperty<>(
1075 NbBundle.getMessage(BlackboardArtifactNode.class,
1076 "ContentTagNode.createSheet.fileCreatedTime.name"),
1077 NbBundle.getMessage(BlackboardArtifactNode.class,
1078 "ContentTagNode.createSheet.fileCreatedTime.displayName"),
1079 "",
1080 file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
1081 sheetSet.put(new NodeProperty<>(
1082 NbBundle.getMessage(BlackboardArtifactNode.class,
1083 "ContentTagNode.createSheet.fileSize.name"),
1084 NbBundle.getMessage(BlackboardArtifactNode.class,
1085 "ContentTagNode.createSheet.fileSize.displayName"),
1086 "",
1087 file == null ? "" : file.getSize()));
1088 sheetSet.put(new NodeProperty<>(
1089 Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
1090 Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
1091 "",
1092 file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
1093 }
1094 } else {
1095 String dataSourceStr = "";
1096 try {
1097 Content dataSource = srcContent.getDataSource();
1098 if (dataSource != null) {
1099 dataSourceStr = dataSource.getName();
1100 } else {
1101 dataSourceStr = getRootAncestorName();
1102 }
1103 } catch (TskCoreException ex) {
1104 logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1105
1106 }
1107
1108 if (dataSourceStr.isEmpty() == false) {
1109 sheetSet.put(new NodeProperty<>(
1110 NbBundle.getMessage(BlackboardArtifactNode.class,
1111 "BlackboardArtifactNode.createSheet.dataSrc.name"),
1112 NbBundle.getMessage(BlackboardArtifactNode.class,
1113 "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
1114 NO_DESCR,
1115 dataSourceStr));
1116 }
1117 }
1118
1119 /*
1120 * If the artifact represented by this node is an EXIF artifact, add the
1121 * source file size and path to the sheet.
1122 */
1123 if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
1124 long size = 0;
1125 String path = ""; //NON-NLS
1126 if (srcContent instanceof AbstractFile) {
1127 AbstractFile af = (AbstractFile) srcContent;
1128 size = af.getSize();
1129 try {
1130 path = af.getUniquePath();
1131 } catch (TskCoreException ex) {
1132 path = af.getParentPath();
1133
1134 }
1135 }
1136 sheetSet.put(new NodeProperty<>(
1137 NbBundle.getMessage(BlackboardArtifactNode.class,
1138 "BlackboardArtifactNode.createSheet.fileSize.name"),
1139 NbBundle.getMessage(BlackboardArtifactNode.class,
1140 "BlackboardArtifactNode.createSheet.fileSize.displayName"),
1141 NO_DESCR,
1142 size));
1143 sheetSet
1144 .put(new NodeProperty<>(
1145 NbBundle.getMessage(BlackboardArtifactNode.class,
1146 "BlackboardArtifactNode.createSheet.path.name"),
1147 NbBundle.getMessage(BlackboardArtifactNode.class,
1148 "BlackboardArtifactNode.createSheet.path.displayName"),
1149 NO_DESCR,
1150 path));
1151 }
1152
1153 if (scoTask != null) {
1154 backgroundTasksPool.submit(scoTask);
1155 }
1156
1157 for (ArtifactPropertyEnricher enricher : ENRICHERS) {
1158 try {
1159 Optional<Sheet.Set> enrichmentSet = enricher.getEnrichment(artifact);
1160 enrichmentSet.ifPresent(s -> {
1161 if (sheet.get(s.getName()) != null) {
1162 logger.log(Level.WARNING, String.format("Enricher %s returned a Sheet.Set with duplicate name '%s' for artifact %d; skipping to avoid overwriting existing properties",
1163 enricher.getClass().getName(), s.getName(), artifact.getArtifactID()));
1164 } else {
1165 sheet.put(s);
1166 }
1167 });
1168 } catch (Exception ex) {
1169 logger.log(Level.WARNING, String.format("Error getting property enrichment from %s for artifact %d",
1170 enricher.getClass().getName(), artifact.getArtifactID()), ex);
1171 }
1172 }
1173
1174 return sheet;
1175 }
1176
1183 @Override
1184 protected final List<Tag> getAllTagsFromDatabase() {
1185 List<Tag> tags = new ArrayList<>();
1186 try {
1189 } catch (TskCoreException | NoCurrentCaseException ex) {
1190 logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
1191 }
1192 return tags;
1193 }
1194
1211 @Override
1212 protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
1213 /*
1214 * Has a tag with a comment been applied to the artifact or its source
1215 * content?
1216 */
1218 for (Tag tag : tags) {
1219 if (!StringUtils.isBlank(tag.getComment())) {
1221 break;
1222 }
1223 }
1224 /*
1225 * Is there a comment in the CR for anything that matches the value and
1226 * type of the specified attributes.
1227 */
1228 try {
1232 } else {
1234 }
1235 }
1236 } catch (CentralRepoException ex) {
1237 logger.log(Level.SEVERE, "Attempted to Query CR for presence of comments in a Blackboard Artifact node and was unable to perform query, comment column will only reflect caseDB", ex);
1238 }
1239 return status;
1240 }
1241
1242 @Override
1243 protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription) {
1244 Long count = -1L;
1245 String description = defaultDescription;
1246 try {
1247 if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
1249 description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attribute.getCorrelationType().getDisplayName());
1250 } else if (attribute != null) {
1251 description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
1252 }
1253 } catch (CentralRepoException ex) {
1254 logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), attribute.getCorrelationType(), attribute.getCorrelationValue()), ex);
1256 logger.log(Level.WARNING, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), attribute.getCorrelationType(), attribute.getCorrelationValue()), ex);
1257 }
1258 return Pair.of(count, description);
1259 }
1260
1264 private void updateSheet() {
1265 SwingUtilities.invokeLater(() -> {
1266 this.setSheet(createSheet());
1267 });
1268 }
1269
1276 private String getRootAncestorName() {
1277 String parentName = srcContent.getName();
1278 Content parent = srcContent;
1279 try {
1280 while ((parent = parent.getParent()) != null) {
1281 parentName = parent.getName();
1282 }
1283 } catch (TskCoreException ex) {
1284 logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1285 return "";
1286 }
1287 return parentName;
1288 }
1289
1296 public void addNodeProperty(NodeProperty<?> property) {
1297 if (customProperties == null) {
1298 customProperties = new ArrayList<>();
1299 }
1300 customProperties.add(property);
1301 }
1302
1311 @SuppressWarnings("deprecation")
1312 private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
1313 try {
1314 for (BlackboardAttribute attribute : artifact.getAttributes()) {
1315 final int attributeTypeID = attribute.getAttributeType().getTypeID();
1316 if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
1317 || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
1318 || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
1319 || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
1320 || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
1321 || attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
1322 /*
1323 * For Cyber Triage's CT_JSON_DATA_ATTRIBUTE, parse the JSON
1324 * and expand each field into the property map. All other
1325 * skipped attributes (including other JSON types) are ignored.
1326 */
1327 if (attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON
1329 attribute.getAttributeType().getTypeName())) {
1330 CyberTriageData.addCtJsonProperties(map, attribute.getValueString());
1331 }
1332 } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
1333 addEmailMsgProperty(map, attribute);
1334 } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1335 map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1336 } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
1337 && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
1338 /*
1339 * The truncation of text attributes appears to have been
1340 * motivated by the statement that "RegRipper output would
1341 * often cause the UI to get a black line accross it and
1342 * hang if you hovered over large output or selected it.
1343 * This reduces the amount of data in the table. Could
1344 * consider doing this for all fields in the UI."
1345 */
1346 String value = attribute.getDisplayString();
1347 if (value.length() > 512) {
1348 value = value.substring(0, 512);
1349 }
1350 map.put(attribute.getAttributeType().getDisplayName(), value);
1351 } else {
1352 switch (attribute.getAttributeType().getValueType()) {
1353 case INTEGER:
1354 map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
1355 break;
1356 case DOUBLE:
1357 map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
1358 break;
1359 case LONG:
1360 map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
1361 break;
1362 default:
1363 map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1364
1365 }
1366
1367 }
1368 }
1369 } catch (TskCoreException ex) {
1370 logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
1371 }
1372 }
1373
1383 private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
1384 final int attributeTypeID = attribute.getAttributeType().getTypeID();
1385 if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
1386 || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
1387 || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
1388 || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
1389 || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
1390 || attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
1391 /*
1392 * Do nothing.
1393 */
1394 } else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
1395 String value = attribute.getDisplayString();
1396 if (value.length() > 160) {
1397 value = value.substring(0, 160) + "...";
1398 }
1399 map.put(attribute.getAttributeType().getDisplayName(), value);
1400 } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
1401 map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
1402 } else {
1403 map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
1404 }
1405 }
1406
1407 @Override
1408 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
1409 return visitor.visit(this);
1410 }
1411
1412 @Override
1413 public boolean isLeafTypeNode() {
1414 return true;
1415 }
1416
1417 @Override
1418 public String getItemType() {
1419 return getClass().getName();
1420 }
1421
1422 @Override
1423 public <T> T accept(ContentNodeVisitor<T> visitor) {
1424 return visitor.visit(this);
1425 }
1426
1427 @Messages({
1428 "BlackboardArtifactNode_analysisSheet_sourceType_name=Source Type",
1429 "BlackboardArtifactNode_analysisSheet_soureName_name=Source Name",
1430 "BlackboardArtifactNode_analysisSheet_score_name=Score",
1431 "BlackboardArtifactNode_analysisSheet_conclusion_name=Conclusion",
1432 "BlackboardArtifactNode_analysisSheet_configuration_name=Configuration",
1433 "BlackboardArtifactNode_analysisSheet_justifaction_name=Justification"
1434 })
1435
1442 private GetSCOTask updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet) {
1443 sheetSet.put(new NodeProperty<>(
1444 Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1445 Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
1446 NO_DESCR,
1447 getDisplayName()));
1448
1449 GetSCOTask task = addSCOColumns(sheetSet);
1450
1451 sheetSet.put(new NodeProperty<>(
1452 Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1453 Bundle.BlackboardArtifactNode_analysisSheet_sourceType_name(),
1454 NO_DESCR,
1456
1457 sheetSet.put(new NodeProperty<>(
1458 Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1459 Bundle.BlackboardArtifactNode_analysisSheet_score_name(),
1460 NO_DESCR,
1461 result.getScore().getSignificance().getDisplayName()));
1462
1463 sheetSet.put(new NodeProperty<>(
1464 Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1465 Bundle.BlackboardArtifactNode_analysisSheet_conclusion_name(),
1466 NO_DESCR,
1467 result.getConclusion()));
1468
1469 sheetSet.put(new NodeProperty<>(
1470 Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1471 Bundle.BlackboardArtifactNode_analysisSheet_configuration_name(),
1472 NO_DESCR,
1473 result.getConfiguration()));
1474
1475 sheetSet.put(new NodeProperty<>(
1476 Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1477 Bundle.BlackboardArtifactNode_analysisSheet_justifaction_name(),
1478 NO_DESCR,
1479 result.getJustification()));
1480
1481 return task;
1482 }
1483
1484 private GetSCOTask addSCOColumns(Sheet.Set sheetSet) {
1486 /*
1487 * Add S(core), C(omments), and O(ther occurences) columns to the
1488 * sheet and start a background task to compute the value of these
1489 * properties for the artifact represented by this node. The task
1490 * will fire a PropertyChangeEvent when the computation is completed
1491 * and this node's PropertyChangeListener will update the sheet.
1492 */
1493 sheetSet.put(new NodeProperty<>(
1494 Bundle.BlackboardArtifactNode_createSheet_score_name(),
1495 Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1497 ""));
1498 sheetSet.put(new NodeProperty<>(
1499 Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1500 Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1502 ""));
1504 sheetSet.put(new NodeProperty<>(
1505 Bundle.BlackboardArtifactNode_createSheet_count_name(),
1506 Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1508 ""));
1509 }
1510 return new GetSCOTask(new WeakReference<>(this), weakListener);
1511 }
1512 return null;
1513 }
1514
1525 private String getSourceObjType(Content source) {
1526 if (source instanceof BlackboardArtifact) {
1527 BlackboardArtifact srcArtifact = (BlackboardArtifact) source;
1528 try {
1529 return srcArtifact.getType().getDisplayName();
1530 } catch (TskCoreException ex) {
1531 logger.log(Level.SEVERE, "Failed to get custom artifact type id=" + source.getId(), ex);
1532 }
1533 } else if (srcContent instanceof Volume) {
1534 return TskData.ObjectType.VOL.toString();
1535 } else if (srcContent instanceof AbstractFile) {
1536 return TskData.ObjectType.ABSTRACTFILE.toString();
1537 } else if (srcContent instanceof Image) {
1538 return TskData.ObjectType.IMG.toString();
1539 } else if (srcContent instanceof VolumeSystem) {
1540 return TskData.ObjectType.VS.toString();
1541 } else if (srcContent instanceof OsAccount) {
1542 return TskData.ObjectType.OS_ACCOUNT.toString();
1543 } else if (srcContent instanceof HostAddress) {
1544 return TskData.ObjectType.HOST_ADDRESS.toString();
1545 } else if (srcContent instanceof Pool) {
1546 return TskData.ObjectType.POOL.toString();
1547 }
1548 return "";
1549 }
1550
1556 private void updateSCOColumns(final SCOData scoData) {
1557 // Make sure this happens in the EDT
1558 SwingUtilities.invokeLater(new Runnable() {
1559 @Override
1560 public void run() {
1561 if (scoData.getScoreAndDescription() != null) {
1563 Bundle.BlackboardArtifactNode_createSheet_score_name(),
1564 Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
1565 scoData.getScoreAndDescription().getRight(),
1566 scoData.getScoreAndDescription().getLeft()));
1567 }
1568 if (scoData.getComment() != null) {
1570 Bundle.BlackboardArtifactNode_createSheet_comment_name(),
1571 Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
1572 NO_DESCR, scoData.getComment()));
1573 }
1574 if (scoData.getCountAndDescription() != null) {
1576 Bundle.BlackboardArtifactNode_createSheet_count_name(),
1577 Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
1578 scoData.getCountAndDescription().getRight(),
1579 scoData.getCountAndDescription().getLeft()));
1580 }
1581 }
1582 });
1583 }
1584
1589 if (srcContent instanceof BlackboardArtifact) {
1590 try {
1591 setDisplayName(((BlackboardArtifact) srcContent).getShortDescription());
1592 } catch (TskCoreException ex) {
1593 // Log the error, but set the display name to
1594 // Content.getName so there is something visible to the user.
1595 logger.log(Level.WARNING, "Failed to get short description for artifact id = " + srcContent.getId(), ex);
1596 setDisplayName(srcContent.getName());
1597 }
1598 } else if (srcContent instanceof OsAccount) {
1599 setDisplayName(((OsAccount) srcContent).getAddr().orElse(srcContent.getName()));
1600 } else {
1601 setDisplayName(srcContent.getName());
1602 }
1603
1604 setShortDescription(getDisplayName());
1605 }
1606
1619 @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
1620 "BlackboardArtifactNode.createSheet.score.displayName=S",
1621 "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
1622 "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
1623 "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
1624 "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
1625 "BlackboardArtifactNode.createSheet.noScore.description=No score"})
1626 @Deprecated
1627 protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
1628 Pair<Score, String> scoreAndDescription = getScorePropertyAndDescription();
1629 sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
1630 }
1631
1641 @NbBundle.Messages({
1642 "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
1643 )
1644 @Deprecated
1645 protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
1646 List<Tag> tags = new ArrayList<>();
1647 try {
1650 } catch (TskCoreException | NoCurrentCaseException ex) {
1651 logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
1652 }
1653 sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1654 }
1655
1667 @Deprecated
1668 protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
1669 sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
1670 }
1671
1684 @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
1685 "BlackboardArtifactNode.createSheet.count.displayName=O",
1686 "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
1687 "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
1688 "# {0} - occurrenceCount",
1689 "# {1} - attributeType",
1690 "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
1691 @Deprecated
1692 protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
1693 Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute, Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
1694 sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
1695 }
1696
1711 @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
1712 "BlackboardArtifactNode.createSheet.comment.displayName=C"})
1713 @Deprecated
1714 protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
1715 List<CorrelationAttributeInstance> attributes = new ArrayList<>();
1716 attributes.add(attribute);
1717 HasCommentStatus status = getCommentProperty(tags, attributes);
1718 sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
1719 }
1720}
static synchronized AddBlackboardArtifactTagAction getInstance()
static synchronized AddContentTagAction getInstance()
static synchronized DeleteFileContentTagAction getInstance()
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:756
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:711
List< ContentTag > getContentTagsByContent(Content content)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)
static boolean commentExistsOnAttributes(List< CorrelationAttributeInstance > attributes)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)
void addEmailMsgProperty(Map< String, Object > map, BlackboardAttribute attribute)
final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute)
final void addCommentProperty(Sheet.Set sheetSet, List< Tag > tags, CorrelationAttributeInstance attribute)
void fillPropertyMap(Map< String, Object > map, BlackboardArtifact artifact)
BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup)
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, List< CorrelationAttributeInstance > attributes)
static Content getPathIdFile(BlackboardArtifact artifact)
BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath)
String getAssociatedTypeStr(BlackboardArtifact.Type artifactType)
Pair< Long, String > getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription)
static final java.util.Collection<? extends ArtifactPropertyEnricher > ENRICHERS
List< Action > getSrcContentViewerActions(Node srcFileNode, int selectedFileCount)
Action getViewSrcContentAction(BlackboardArtifact artifact, Content content)
GetSCOTask updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet)
final void addScorePropertyAndDescription(Sheet.Set sheetSet, List< Tag > tags)
static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile)
final void addTagProperty(Sheet.Set sheetSet, List< Tag > tags)
List< Action > getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType)
static BlackboardArtifact.Type getType(BlackboardArtifact artifact)
List< Action > getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount)
static void addCtJsonProperties(Map< String, Object > map, String json)
static List< Action > getActions(File file, boolean isArtifactSource)
static synchronized ExportCSVAction getInstance()
static synchronized ExtractAction getInstance()
Long getCountCasesWithOtherInstances(CorrelationAttributeInstance instance)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.