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