Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
Artifacts.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2023 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.datamodel;
20
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.util.Collections;
24import java.util.EnumSet;
25import java.util.HashMap;
26import java.util.List;
27import java.util.Map;
28import java.util.Objects;
29import java.util.Set;
30import java.util.logging.Level;
31import java.util.stream.Collectors;
32import java.util.stream.Stream;
33import org.openide.nodes.ChildFactory;
34import org.openide.nodes.Children;
35import org.openide.nodes.Node;
36import org.openide.nodes.Sheet;
37import org.openide.util.Lookup;
38import org.openide.util.NbBundle;
39import org.openide.util.WeakListeners;
40import org.openide.util.lookup.Lookups;
41import org.sleuthkit.autopsy.casemodule.Case;
42import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
43import org.sleuthkit.autopsy.coreutils.Logger;
44import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
45import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
46import org.sleuthkit.autopsy.ingest.IngestManager;
47import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
48import org.sleuthkit.datamodel.BlackboardArtifact;
49import org.sleuthkit.datamodel.SleuthkitCase;
50import org.sleuthkit.datamodel.TskCoreException;
51import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
52import org.sleuthkit.datamodel.BlackboardArtifact.Category;
53import org.python.google.common.collect.Sets;
54import org.sleuthkit.datamodel.Blackboard;
55import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
56import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
57import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
58import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
59import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
60import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
61import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
62import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
63import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ITEM;
64import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
65import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
66import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
67import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_MALWARE;
68
72public class Artifacts {
73
76
80 static class BaseArtifactNode extends DisplayableItemNode {
81
90 BaseArtifactNode(Children children, String icon, String name, String displayName) {
91 super(children, Lookups.singleton(name));
92 super.setName(name);
93 super.setDisplayName(displayName);
94 this.setIconBaseWithExtension(icon); //NON-NLS
95 }
96
97 @Override
98 public boolean isLeafTypeNode() {
99 return false;
100 }
101
102 @Override
103 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
104 return visitor.visit(this);
105 }
106
107 @Override
108 protected Sheet createSheet() {
109 Sheet sheet = super.createSheet();
110 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
111 if (sheetSet == null) {
112 sheetSet = Sheet.createPropertiesSet();
113 sheet.put(sheetSet);
114 }
115
116 sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
117 NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
118 NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
119 super.getDisplayName()));
120 return sheet;
121 }
122
123 @Override
124 public String getItemType() {
125 return getClass().getName();
126 }
127 }
128
132 private static class TypeNodeKey {
133
135 private final Set<BlackboardArtifact.Type> applicableTypes;
136
145 TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
146 this(new TypeNode(type, dsObjId), type);
147 }
148
156 TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
157 this.node = typeNode;
158 this.applicableTypes = Stream.of(types)
159 .filter(t -> t != null)
160 .collect(Collectors.toSet());
161 }
162
168 UpdatableCountTypeNode getNode() {
169 return node;
170 }
171
177 Set<BlackboardArtifact.Type> getApplicableTypes() {
178 return applicableTypes;
179 }
180
181 @Override
182 public int hashCode() {
183 int hash = 3;
184 hash = 61 * hash + Objects.hashCode(this.applicableTypes);
185 return hash;
186 }
187
188 @Override
189 public boolean equals(Object obj) {
190 if (this == obj) {
191 return true;
192 }
193 if (obj == null) {
194 return false;
195 }
196 if (getClass() != obj.getClass()) {
197 return false;
198 }
199 final TypeNodeKey other = (TypeNodeKey) obj;
200 if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
201 return false;
202 }
203 return true;
204 }
205
206 }
207
212 static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
213
214 private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
215
219 @SuppressWarnings("deprecation")
220 private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
221 // these are shown in other parts of the UI (and different node types)
222 TSK_DATA_SOURCE_USAGE,
223 TSK_GEN_INFO,
224 new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
225 TSK_TL_EVENT,
226 //This is not meant to be shown in the UI at all. It is more of a meta artifact.
227 TSK_ASSOCIATED_OBJECT
228 );
229
244 @SuppressWarnings("deprecation")
245 private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
246
247 int typeId = type.getTypeID();
248 if (TSK_EMAIL_MSG.getTypeID() == typeId) {
249 EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
250 return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
251
252 } else if (TSK_ACCOUNT.getTypeID() == typeId) {
253 Accounts.AccountsRootNode accountsNode = new Accounts(dsObjId).new AccountsRootNode();
254 return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
255
256 } else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
257 KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
258 return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
259
260 } else if (TSK_INTERESTING_ITEM.getTypeID() == typeId) {
261 InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ITEM, dsObjId).new RootNode();
262 return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ITEM);
263 } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId) {
264 InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode();
265 return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT);
266 } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
267 InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode();
268 return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT);
269 } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
270 HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
271 return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
272 } else if (TSK_MALWARE.getTypeID() == typeId) {
273 MalwareHits.RootNode malwareHits = new MalwareHits(skCase, dsObjId).new RootNode();
274 return new TypeNodeKey(malwareHits, TSK_MALWARE);
275 } else {
276 return new TypeNodeKey(type, dsObjId);
277 }
278 }
279
280 // maps the artifact type to its child node
281 private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
282 private final long filteringDSObjId;
283
289 private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
290 private final Category category;
291
292 private final PropertyChangeListener weakPcl;
293
302 TypeFactory(Category category, long filteringDSObjId) {
303 super();
304 this.filteringDSObjId = filteringDSObjId;
305 this.category = category;
306
307 PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
308 String eventType = evt.getPropertyName();
309 if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
310 // case was closed. Remove listeners so that we don't get called with a stale case handle
311 if (evt.getNewValue() == null) {
312 removeNotify();
313 }
314 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
315 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
322 try {
323 Case.getCurrentCaseThrows();
324 refresh(false);
325 } catch (NoCurrentCaseException notUsed) {
329 }
330 }
331 };
332
333 weakPcl = WeakListeners.propertyChange(pcl, null);
334 }
335
336 @Override
337 protected void addNotify() {
338 super.addNotify();
339 refreshThrottler.registerForIngestModuleEvents();
340 IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
341 Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
342 }
343
344 @Override
345 protected void finalize() throws Throwable {
346 super.finalize();
347 refreshThrottler.unregisterEventListener();
348 IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
349 Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
350 typeNodeMap.clear();
351 }
352
353 @Override
354 protected boolean createKeys(List<TypeNodeKey> list) {
355 try {
356 // Get all types in use
357 SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
358 List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
359 ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
360 : skCase.getArtifactTypesInUse();
361
362 List<TypeNodeKey> allKeysSorted = types.stream()
363 // filter types by category and ensure they are not in the list of ignored types
364 .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
365 .map(tp -> {
366 // if typeNodeMap already contains key, update the relevant node and return the node
367 if (typeNodeMap.containsKey(tp)) {
368 TypeNodeKey typeKey = typeNodeMap.get(tp);
369 typeKey.getNode().updateDisplayName();
370 return typeKey;
371 } else {
372 // if key is not in map, create the type key and add to map
373 TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
374 for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
375 typeNodeMap.put(recordType, newTypeKey);
376 }
377 return newTypeKey;
378 }
379 })
380 // ensure record is returned
381 .filter(record -> record != null)
382 // there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
383 // ensure the keys are distinct
384 .distinct()
385 // sort by display name
386 .sorted((a, b) -> {
387 String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
388 String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
389 return aSafe.compareToIgnoreCase(bSafe);
390 })
391 .collect(Collectors.toList());
392
393 list.addAll(allKeysSorted);
394
395 } catch (NoCurrentCaseException ex) {
396 logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
397 } catch (TskCoreException ex) {
398 logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
399 }
400 return true;
401 }
402
403 @Override
404 protected Node createNodeForKey(TypeNodeKey key) {
405 return key.getNode();
406 }
407
408 @Override
409 public void refresh() {
410 refresh(false);
411 }
412
413 @Override
414 public boolean isRefreshRequired(PropertyChangeEvent evt) {
415 String eventType = evt.getPropertyName();
416 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
422 try {
423 Case.getCurrentCaseThrows();
429 final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
430 if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
431 && !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
432 return true;
433 }
434 } catch (NoCurrentCaseException notUsed) {
438 }
439 }
440 return false;
441 }
442 }
443
448 public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
449
450 private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
451
452 private final Set<BlackboardArtifact.Type> types;
453 private final long filteringDSObjId;
454 private long childCount = 0;
455 private final String baseName;
456
469 public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
470 long filteringDSObjId, BlackboardArtifact.Type... types) {
471
472 super(children, lookup);
473 this.types = Stream.of(types).collect(Collectors.toSet());
474 this.filteringDSObjId = filteringDSObjId;
475 this.baseName = baseName;
476 updateDisplayName();
477 }
478
484 protected long getChildCount() {
485 return this.childCount;
486 }
487
497 protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
498 int count = 0;
499 for (BlackboardArtifact.Type type : this.types) {
500 if (filteringDSObjId > 0) {
501 count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
502 } else {
503 count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
504 }
505 }
506 return count;
507 }
508
513 void updateDisplayName() {
514 try {
515 SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
516 this.childCount = fetchChildCount(skCase);
517 } catch (NoCurrentCaseException ex) {
518 logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
519 } catch (TskCoreException ex) {
520 logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
521 }
522 super.setDisplayName(this.baseName + " (" + this.childCount + ")");
523 }
524 }
525
532 static class TypeNode extends UpdatableCountTypeNode {
533
534 private final BlackboardArtifact.Type type;
535
544 TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
545 super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
546 Lookups.singleton(type.getDisplayName()),
547 type.getDisplayName(),
549 type);
550
551 super.setName(type.getTypeName());
552 this.type = type;
553 String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
554 setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
555 }
556
557 @Override
558 protected Sheet createSheet() {
559 Sheet sheet = super.createSheet();
560 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
561 if (sheetSet == null) {
562 sheetSet = Sheet.createPropertiesSet();
563 sheet.put(sheetSet);
564 }
565
566 sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
567 NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
568 NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
569 type.getDisplayName()));
570
571 sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
572 NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
573 NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
574 getChildCount()));
575
576 return sheet;
577 }
578
579 @Override
580 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
581 return visitor.visit(this);
582 }
583
584 @Override
585 public boolean isLeafTypeNode() {
586 return true;
587 }
588
589 @Override
590 public String getItemType() {
591 return getClass().getName() + type.getDisplayName();
592 }
593 }
594
598 private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
599
600 private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
601 private final BlackboardArtifact.Type type;
602
609 private final long filteringDSObjId;
610
619 ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
620 super(type.getTypeName());
621 this.type = type;
622 this.filteringDSObjId = filteringDSObjId;
623 }
624
625 private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
626 String eventType = evt.getPropertyName();
627 if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
628 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
635 try {
637 refresh(false);
638 } catch (NoCurrentCaseException notUsed) {
642 }
643 }
644 };
645
646 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
647
648 @Override
649 protected void onAdd() {
650 refreshThrottler.registerForIngestModuleEvents();
652 }
653
654 @Override
655 protected void onRemove() {
656 if (refreshThrottler != null) {
657 refreshThrottler.unregisterEventListener();
658 }
660 }
661
662 @Override
663 protected Node createNodeForKey(BlackboardArtifact key) {
664 return new BlackboardArtifactNode(key);
665 }
666
667 @Override
668 protected List<BlackboardArtifact> makeKeys() {
669 try {
670 List<? extends BlackboardArtifact> arts;
671 Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
672 switch (this.type.getCategory()) {
673
674 case ANALYSIS_RESULT:
675 arts = (filteringDSObjId > 0)
676 ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
677 : blackboard.getAnalysisResultsByType(type.getTypeID());
678 break;
679 case DATA_ARTIFACT:
680 default:
681 arts = (filteringDSObjId > 0)
682 ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
683 : blackboard.getDataArtifacts(type.getTypeID());
684 break;
685 }
686
687 for (BlackboardArtifact art : arts) {
688 //Cache attributes while we are off the EDT.
689 //See JIRA-5969
690 art.getAttributes();
691 }
692
693 @SuppressWarnings("unchecked")
694 List<BlackboardArtifact> toRet = (List<BlackboardArtifact>) (List<?>) arts;
695 return toRet;
696 } catch (NoCurrentCaseException ex) {
697 logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
698 } catch (TskCoreException ex) {
699 logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
700 }
701 return Collections.emptyList();
702 }
703
704 @Override
705 public void refresh() {
706 refresh(false);
707 }
708
709 @Override
710 public boolean isRefreshRequired(PropertyChangeEvent evt) {
711 String eventType = evt.getPropertyName();
712 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
713
720 try {
728 final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
729 if (null != event && event.getBlackboardArtifactType().equals(type)) {
730 return true;
731 }
732
733 } catch (NoCurrentCaseException notUsed) {
737 }
738 }
739 return false;
740 }
741 }
742}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
final Set< BlackboardArtifact.Type > applicableTypes
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
static synchronized IngestManager getInstance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)

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