From a24006cae49c28f4d7d1ee4ed838acf8b3b2bb38 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 7 Sep 2021 11:39:45 -0400 Subject: [PATCH 1/6] work towards interesting hit changes --- .../autopsy/datamodel/InterestingHits.java | 268 +++++++++--------- .../DirectoryTreeTopComponent.java | 55 ++-- 2 files changed, 174 insertions(+), 149 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index f636264082..0e8bbb2a8a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -34,6 +34,8 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -62,6 +64,11 @@ public class InterestingHits implements AutopsyVisitableItem { private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); + private static final Map ART_TYPES = Stream.of( + BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, + BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT + ).collect(Collectors.toMap(tp -> tp.getTypeID(), tp -> tp, (tp1, tp2) -> tp1)); + private SleuthkitCase skCase; private final InterestingResults interestingResults = new InterestingResults(); private final long filteringDSObjId; // 0 if not filtering/grouping by data source @@ -92,20 +99,21 @@ public class InterestingHits implements AutopsyVisitableItem { private class InterestingResults extends Observable { // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized - private final Map>> interestingItemsMap = new LinkedHashMap<>(); + private final Map>> interestingItemsMap = new LinkedHashMap<>(); - public List getSetNames() { + public List getSetNames(BlackboardArtifact.Type type) { List setNames; synchronized (interestingItemsMap) { - setNames = new ArrayList<>(interestingItemsMap.keySet()); + Map> setMapping = interestingItemsMap.getOrDefault(type, Collections.emptyMap()); + setNames = new ArrayList<>(setMapping.keySet()); } Collections.sort(setNames); return setNames; } - public Set getArtifactIds(String setName, String typeName) { + public Set getArtifactIds(BlackboardArtifact.Type type, String setName) { synchronized (interestingItemsMap) { - return interestingItemsMap.get(setName).get(typeName); + return interestingItemsMap.getOrDefault(type, Collections.emptyMap()).getOrDefault(setName, Collections.emptySet()); } } @@ -113,8 +121,7 @@ public class InterestingHits implements AutopsyVisitableItem { synchronized (interestingItemsMap) { interestingItemsMap.clear(); } - loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT); - loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT); + loadArtifacts(); setChanged(); notifyObservers(); } @@ -124,18 +131,22 @@ public class InterestingHits implements AutopsyVisitableItem { * the interestingItemsMap */ @SuppressWarnings("deprecation") - private void loadArtifacts(BlackboardArtifact.Type artType) { + private void loadArtifacts() { if (skCase == null) { return; } int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); - int artId = artType.getTypeID(); - String query = "SELECT value_text,blackboard_artifacts.artifact_obj_id,attribute_type_id " //NON-NLS + + String typeIds = ART_TYPES.keySet().stream() + .map(id -> id.toString()) + .collect(Collectors.joining(", ")); + + String query = "SELECT value_text, blackboard_artifacts.artifact_obj_id, artifact_type_id " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS - + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS + + " AND blackboard_artifacts.artifact_type_id IN (" + typeIds + ")"; //NON-NLS if (filteringDSObjId > 0) { query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } @@ -144,14 +155,14 @@ public class InterestingHits implements AutopsyVisitableItem { synchronized (interestingItemsMap) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { + int artTypeId = resultSet.getInt("artifact_type_id"); + BlackboardArtifact.Type artType = ART_TYPES.get(artTypeId); String value = resultSet.getString("value_text"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS - if (!interestingItemsMap.containsKey(value)) { - interestingItemsMap.put(value, new LinkedHashMap<>()); - interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>()); - interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>()); - } - interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactObjId); + interestingItemsMap + .computeIfAbsent(artType, (k) -> new LinkedHashMap<>()) + .computeIfAbsent(value, (k) -> new HashSet<>()) + .add(artifactObjId); } } } catch (TskCoreException | SQLException ex) { @@ -171,7 +182,7 @@ public class InterestingHits implements AutopsyVisitableItem { public class RootNode extends UpdatableCountTypeNode { public RootNode() { - super(Children.create(new SetNameFactory(), true), + super(Children.create(new HitTypeFactory(), true), Lookups.singleton(DISPLAY_NAME), DISPLAY_NAME, filteringDSObjId, @@ -216,6 +227,94 @@ public class InterestingHits implements AutopsyVisitableItem { private class SetNameFactory extends ChildFactory.Detachable implements Observer { + private final BlackboardArtifact.Type type; + + public SetNameFactory(BlackboardArtifact.Type type) { + this.type = type; + } + + @Override + protected boolean createKeys(List list) { + list.addAll(interestingResults.getSetNames(this.type)); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new SetNameNode(this.type, key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + } + + public class SetNameNode extends DisplayableItemNode implements Observer { + + private final BlackboardArtifact.Type type; + private final String setName; + + public SetNameNode(BlackboardArtifact.Type type, String setName) {//, Set children) { + super(Children.create(new HitFactory(type, setName), true), Lookups.singleton(setName)); + this.setName = setName; + this.type = type; + super.setName(setName); + updateDisplayName(); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS + interestingResults.addObserver(this); + } + + private void updateDisplayName() { + int sizeOfSet = interestingResults.getArtifactIds(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, setName).size() + + interestingResults.getArtifactIds(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, setName).size(); + super.setDisplayName(setName + " (" + sizeOfSet + ")"); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.desc"), + getName())); + + return sheet; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } + + @Override + public String getItemType() { + /** + * For custom settings for each rule set, return + * getClass().getName() + setName instead. + */ + return getClass().getName(); + } + } + + private class HitTypeFactory extends ChildFactory.Detachable implements Observer { + /* * This should probably be in the top-level class, but the factory has * nice methods for its startup and shutdown, so it seemed like a @@ -272,9 +371,14 @@ public class InterestingHits implements AutopsyVisitableItem { } } }; - + private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); - + + private HitTypeFactory() { + super(); + interestingResults.addObserver(this); + } + @Override protected void addNotify() { IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); @@ -294,104 +398,15 @@ public class InterestingHits implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { - list.addAll(interestingResults.getSetNames()); + protected boolean createKeys(List list) { + list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT); + list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT); return true; } @Override - protected Node createNodeForKey(String key) { - return new SetNameNode(key); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - } - - public class SetNameNode extends DisplayableItemNode implements Observer { - - private final String setName; - - public SetNameNode(String setName) {//, Set children) { - super(Children.create(new HitTypeFactory(setName), true), Lookups.singleton(setName)); - this.setName = setName; - super.setName(setName); - updateDisplayName(); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS - interestingResults.addObserver(this); - } - - private void updateDisplayName() { - int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size() - + interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()).size(); - super.setDisplayName(setName + " (" + sizeOfSet + ")"); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.desc"), - getName())); - - return sheet; - } - - @Override - public T accept(DisplayableItemNodeVisitor visitor) { - return visitor.visit(this); - } - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } - - @Override - public String getItemType() { - /** - * For custom settings for each rule set, return - * getClass().getName() + setName instead. - */ - return getClass().getName(); - } - } - - private class HitTypeFactory extends ChildFactory implements Observer { - - private final String setName; - private final Map artifactHits = new HashMap<>(); - - private HitTypeFactory(String setName) { - super(); - this.setName = setName; - interestingResults.addObserver(this); - } - - @Override - protected boolean createKeys(List list) { - list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()); - list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()); - return true; - } - - @Override - protected Node createNodeForKey(String key) { - return new InterestingItemTypeNode(setName, key); + protected Node createNodeForKey(BlackboardArtifact.Type key) { + return new InterestingItemTypeNode(key); } @Override @@ -402,26 +417,25 @@ public class InterestingHits implements AutopsyVisitableItem { public class InterestingItemTypeNode extends DisplayableItemNode implements Observer { - private final String typeName; - private final String setName; + private final BlackboardArtifact.Type type; + + private InterestingItemTypeNode(BlackboardArtifact.Type type) { + super(Children.create(new SetNameFactory(type), true), Lookups.singleton(type)); + this.type = type; - private InterestingItemTypeNode(String setName, String typeName) { - super(Children.create(new HitFactory(setName, typeName), true), Lookups.singleton(setName)); - this.typeName = typeName; - this.setName = setName; /** * We use the combination of setName and typeName as the name of the * node to ensure that nodes have a unique name. This comes into * play when associating paging state with the node. */ - super.setName(setName + "_" + typeName); + super.setName(type.getDisplayName()); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS interestingResults.addObserver(this); } private void updateDisplayName() { - super.setDisplayName(typeName + " (" + interestingResults.getArtifactIds(setName, typeName).size() + ")"); + super.setDisplayName(type.getDisplayName() + " (" + interestingResults.getSetNames(type).size() + ")"); } @Override @@ -466,19 +480,19 @@ public class InterestingHits implements AutopsyVisitableItem { private class HitFactory extends BaseChildFactory implements Observer { + private final BlackboardArtifact.Type type; private final String setName; - private final String typeName; private final Map artifactHits = new HashMap<>(); - private HitFactory(String setName, String typeName) { + private HitFactory(BlackboardArtifact.Type type, String setName) { /** * The node name passed to the parent constructor must be the same * as the name set in the InterestingItemTypeNode constructor, i.e. * setName underscore typeName */ - super(setName + "_" + typeName); + super(setName + "_" + type.getDisplayName()); this.setName = setName; - this.typeName = typeName; + this.type = type; interestingResults.addObserver(this); } @@ -486,7 +500,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected List makeKeys() { if (skCase != null) { - interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { + interestingResults.getArtifactIds(type, setName).forEach((id) -> { try { if (!artifactHits.containsKey(id)) { AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 975a659aa7..258e692f39 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1409,34 +1409,45 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) { Node interestingItemsRootNode = typesChildren.findChild(NbBundle .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text")); + Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); + String setName = null; try { - String setName = null; - List attributes = art.getAttributes(); - for (BlackboardAttribute att : attributes) { - int typeId = att.getAttributeType().getTypeID(); - if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { - setName = att.getValueString(); - } - } - Node setNode = interestingItemsRootChildren.findChild(setName); - if (setNode == null) { - return null; - } - - Children fileArtifactChildren = setNode.getChildren(); - Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes(); - if (fileArtifactNodes == null || fileArtifactNodes.length != 2) { - return null; - } - - return (art.getArtifactTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) - ? fileArtifactNodes[0] - : fileArtifactNodes[1]; + setName = art.getAttributes().stream() + .filter(attr -> attr.getAttributeType().getTypeID() == BlackboardAttribute.Type.TSK_SET_NAME.getTypeID()) + .map(attr -> attr.getValueString()) + .findFirst() + .orElse(null); + } catch (TskCoreException ex) { LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS return null; } + + // if no set name, no set node will be identified. + if (setName == null) { + return null; + } + + Stream typeNodes = interestingItemsRootChildren != null ? Stream.of(interestingItemsRootChildren.getNodes(true)) : Stream.empty(); + + Children setNodeChildren = typeNodes + .filter((nd) -> { + BlackboardArtifact.Type ndType = nd.getLookup().lookup(BlackboardArtifact.Type.class); + return ndType != null && ndType.getTypeID() == art.getArtifactTypeID(); + }) + .findFirst() + .flatMap(typeNode -> Optional.ofNullable(typeNode.getChildren())) + .orElse(null); + + // set node children for type could not be found, so return null. + if (setNodeChildren == null) { + return null; + } + + // make sure data is fully loaded + setNodeChildren.getNodes(true); + return interestingItemsRootChildren.findChild(setName); } /** From 4e38a562c764eb45242e048d8f3abdbebd9be05b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 7 Sep 2021 12:32:31 -0400 Subject: [PATCH 2/6] commenting --- .../autopsy/datamodel/InterestingHits.java | 72 ++++++++++++++++--- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 0e8bbb2a8a..3bdd47eca3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -96,12 +96,22 @@ public class InterestingHits implements AutopsyVisitableItem { interestingResults.update(); } + /** + * Cache of result ids mapped by artifact type -> set name -> artifact id. + */ private class InterestingResults extends Observable { // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized private final Map>> interestingItemsMap = new LinkedHashMap<>(); - public List getSetNames(BlackboardArtifact.Type type) { + /** + * Returns all the set names for a given interesting item type. + * + * @param type The interesting item type. + * + * @return The set names. + */ + List getSetNames(BlackboardArtifact.Type type) { List setNames; synchronized (interestingItemsMap) { Map> setMapping = interestingItemsMap.getOrDefault(type, Collections.emptyMap()); @@ -111,13 +121,25 @@ public class InterestingHits implements AutopsyVisitableItem { return setNames; } - public Set getArtifactIds(BlackboardArtifact.Type type, String setName) { + /** + * Returns all artifact ids belonging to the specified interesting item + * type and set name. + * + * @param type The interesting item type. + * @param setName The set name. + * + * @return The artifact ids in that set name and type. + */ + Set getArtifactIds(BlackboardArtifact.Type type, String setName) { synchronized (interestingItemsMap) { return interestingItemsMap.getOrDefault(type, Collections.emptyMap()).getOrDefault(setName, Collections.emptySet()); } } - public void update() { + /** + * Triggers a fetch from the database to update this cache. + */ + void update() { synchronized (interestingItemsMap) { interestingItemsMap.clear(); } @@ -225,11 +247,19 @@ public class InterestingHits implements AutopsyVisitableItem { } } + /** + * Creates nodes for all sets for a specified interesting item type. + */ private class SetNameFactory extends ChildFactory.Detachable implements Observer { private final BlackboardArtifact.Type type; - public SetNameFactory(BlackboardArtifact.Type type) { + /** + * Constructor. + * + * @param type The artifact type to filter these sets. + */ + SetNameFactory(BlackboardArtifact.Type type) { this.type = type; } @@ -250,24 +280,26 @@ public class InterestingHits implements AutopsyVisitableItem { } } + /** + * A node for a set to be displayed in the tree. + */ public class SetNameNode extends DisplayableItemNode implements Observer { - private final BlackboardArtifact.Type type; private final String setName; + private final BlackboardArtifact.Type type; public SetNameNode(BlackboardArtifact.Type type, String setName) {//, Set children) { super(Children.create(new HitFactory(type, setName), true), Lookups.singleton(setName)); this.setName = setName; this.type = type; - super.setName(setName); + super.setName(type.getDisplayName() + "_" + setName); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS interestingResults.addObserver(this); } private void updateDisplayName() { - int sizeOfSet = interestingResults.getArtifactIds(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, setName).size() - + interestingResults.getArtifactIds(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, setName).size(); + int sizeOfSet = interestingResults.getArtifactIds(type, setName).size(); super.setDisplayName(setName + " (" + sizeOfSet + ")"); } @@ -313,6 +345,9 @@ public class InterestingHits implements AutopsyVisitableItem { } } + /** + * Shows an Interesting Item type node (i.e. file hit or artifact hit). + */ private class HitTypeFactory extends ChildFactory.Detachable implements Observer { /* @@ -399,8 +434,7 @@ public class InterestingHits implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { - list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT); - list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT); + list.addAll(ART_TYPES.values()); return true; } @@ -415,10 +449,18 @@ public class InterestingHits implements AutopsyVisitableItem { } } + /** + * Parent node for interesting item type that shows child set nodes. + */ public class InterestingItemTypeNode extends DisplayableItemNode implements Observer { private final BlackboardArtifact.Type type; + /** + * Main constructor. + * + * @param type The artifact type to display. + */ private InterestingItemTypeNode(BlackboardArtifact.Type type) { super(Children.create(new SetNameFactory(type), true), Lookups.singleton(type)); this.type = type; @@ -478,12 +520,22 @@ public class InterestingHits implements AutopsyVisitableItem { } } + /** + * Factory for creating individual interesting item BlackboardArtifactNodes. + */ private class HitFactory extends BaseChildFactory implements Observer { private final BlackboardArtifact.Type type; private final String setName; private final Map artifactHits = new HashMap<>(); + /** + * Main constructor. + * + * @param type The Interesting Item type of artifacts to be + * displayed. + * @param setName The set name of artifacts to be displayed. + */ private HitFactory(BlackboardArtifact.Type type, String setName) { /** * The node name passed to the parent constructor must be the same From 927a93084b3fcf78e926baa3567182103cfc8eb3 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 7 Sep 2021 13:11:36 -0400 Subject: [PATCH 3/6] bug fixes --- .../autopsy/datamodel/InterestingHits.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 3bdd47eca3..1646210506 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -120,6 +120,19 @@ public class InterestingHits implements AutopsyVisitableItem { Collections.sort(setNames); return setNames; } + + /** + * Returns all types currently in the map. + * @return The types present in the map. + */ + List getTypes() { + List types; + synchronized (interestingItemsMap) { + types = new ArrayList<>(interestingItemsMap.keySet()); + } + Collections.sort(types, (a,b) -> a.getDisplayName().compareToIgnoreCase(b.getDisplayName())); + return types; + } /** * Returns all artifact ids belonging to the specified interesting item @@ -164,7 +177,7 @@ public class InterestingHits implements AutopsyVisitableItem { .map(id -> id.toString()) .collect(Collectors.joining(", ")); - String query = "SELECT value_text, blackboard_artifacts.artifact_obj_id, artifact_type_id " //NON-NLS + String query = "SELECT value_text, blackboard_artifacts.artifact_obj_id, blackboard_artifacts.artifact_type_id " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS @@ -434,7 +447,7 @@ public class InterestingHits implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { - list.addAll(ART_TYPES.values()); + list.addAll(interestingResults.getTypes()); return true; } @@ -482,7 +495,7 @@ public class InterestingHits implements AutopsyVisitableItem { @Override public boolean isLeafTypeNode() { - return true; + return false; } @Override From 7adfcf4b178df287e625c0d51c776e25a14bfc9c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 8 Sep 2021 14:19:52 -0400 Subject: [PATCH 4/6] fixes --- .../autopsy/datamodel/InterestingHits.java | 20 +++++++++++++++---- .../DirectoryTreeTopComponent.java | 17 +++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 1646210506..4469e76356 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -117,12 +117,13 @@ public class InterestingHits implements AutopsyVisitableItem { Map> setMapping = interestingItemsMap.getOrDefault(type, Collections.emptyMap()); setNames = new ArrayList<>(setMapping.keySet()); } - Collections.sort(setNames); + Collections.sort(setNames, (a, b) -> a.compareToIgnoreCase(b)); return setNames; } - + /** * Returns all types currently in the map. + * * @return The types present in the map. */ List getTypes() { @@ -130,7 +131,7 @@ public class InterestingHits implements AutopsyVisitableItem { synchronized (interestingItemsMap) { types = new ArrayList<>(interestingItemsMap.keySet()); } - Collections.sort(types, (a,b) -> a.getDisplayName().compareToIgnoreCase(b.getDisplayName())); + Collections.sort(types, (a, b) -> a.getDisplayName().compareToIgnoreCase(b.getDisplayName())); return types; } @@ -287,6 +288,17 @@ public class InterestingHits implements AutopsyVisitableItem { return new SetNameNode(this.type, key); } + @Override + protected void addNotify() { + interestingResults.update(); + interestingResults.addObserver(this); + } + + @Override + protected void finalize() throws Throwable { + interestingResults.deleteObserver(this); + } + @Override public void update(Observable o, Object arg) { refresh(true); @@ -318,7 +330,7 @@ public class InterestingHits implements AutopsyVisitableItem { @Override public boolean isLeafTypeNode() { - return false; + return true; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 258e692f39..3732f3c219 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1409,7 +1409,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) { Node interestingItemsRootNode = typesChildren.findChild(NbBundle .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text")); - + Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); String setName = null; try { @@ -1418,17 +1418,17 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat .map(attr -> attr.getValueString()) .findFirst() .orElse(null); - + } catch (TskCoreException ex) { LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS return null; } - + // if no set name, no set node will be identified. if (setName == null) { return null; } - + Stream typeNodes = interestingItemsRootChildren != null ? Stream.of(interestingItemsRootChildren.getNodes(true)) : Stream.empty(); Children setNodeChildren = typeNodes @@ -1444,10 +1444,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat if (setNodeChildren == null) { return null; } - + // make sure data is fully loaded - setNodeChildren.getNodes(true); - return interestingItemsRootChildren.findChild(setName); + final String finalSetName = setName; + return Stream.of(setNodeChildren.getNodes(true)) + .filter(setNode -> finalSetName.equals(setNode.getLookup().lookup(String.class))) + .findFirst() + .orElse(null); } /** From 4bb97995b27dedb89985f2547c7fdbf5a0a0abb1 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 8 Sep 2021 15:33:04 -0400 Subject: [PATCH 5/6] tree structure changes --- .../autopsy/datamodel/Artifacts.java | 40 +- .../datamodel/DisplayableItemNodeVisitor.java | 23 +- .../autopsy/datamodel/InterestingHits.java | 364 ++++++------------ .../DirectoryTreeTopComponent.java | 39 +- 4 files changed, 153 insertions(+), 313 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java index cd0b03fd98..3f08b3f0bb 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java @@ -250,18 +250,16 @@ public class Artifacts { KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode(); return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT); - } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId - || TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) { - - InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode(); - return new TypeNodeKey(interestingHitsNode, - TSK_INTERESTING_ARTIFACT_HIT, - TSK_INTERESTING_FILE_HIT); - + } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId) { + InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode(); + return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT); + } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) { + InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode(); + return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT); } else if (TSK_HASHSET_HIT.getTypeID() == typeId) { HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode(); return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT); - + } else { return new TypeNodeKey(type, dsObjId); } @@ -278,7 +276,7 @@ public class Artifacts { */ private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); private final Category category; - + private final PropertyChangeListener weakPcl; /** @@ -293,7 +291,7 @@ public class Artifacts { super(); this.filteringDSObjId = filteringDSObjId; this.category = category; - + PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { @@ -322,9 +320,9 @@ public class Artifacts { weakPcl = WeakListeners.propertyChange(pcl, null); } - + @Override - protected void addNotify() { + protected void addNotify() { super.addNotify(); refreshThrottler.registerForIngestModuleEvents(); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); @@ -632,7 +630,7 @@ public class Artifacts { } } }; - + private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); @Override @@ -643,7 +641,7 @@ public class Artifacts { @Override protected void onRemove() { - if(refreshThrottler != null) { + if (refreshThrottler != null) { refreshThrottler.unregisterEventListener(); } IngestManager.getInstance().removeIngestJobEventListener(weakPcl); @@ -663,14 +661,14 @@ public class Artifacts { case ANALYSIS_RESULT: arts = (filteringDSObjId > 0) - ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId) - : blackboard.getAnalysisResultsByType(type.getTypeID()); + ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId) + : blackboard.getAnalysisResultsByType(type.getTypeID()); break; case DATA_ARTIFACT: default: arts = (filteringDSObjId > 0) - ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId) - : blackboard.getDataArtifacts(type.getTypeID()); + ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId) + : blackboard.getDataArtifacts(type.getTypeID()); break; } @@ -679,9 +677,9 @@ public class Artifacts { //See JIRA-5969 art.getAttributes(); } - + @SuppressWarnings("unchecked") - List toRet = (List)(List)arts; + List toRet = (List) (List) arts; return toRet; } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 47db7732ea..1acce2bb7e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -181,15 +181,13 @@ public interface DisplayableItemNodeVisitor { T visit(EmptyNode.MessageNode emptyNode); - T visit(InterestingHits.InterestingItemTypeNode aThis); - /* * Attachments */ T visit(AttachmentNode node); - + T visit(OsAccounts.OsAccountNode node); - + T visit(OsAccounts.OsAccountListNode node); T visit(PersonNode node); @@ -197,12 +195,12 @@ public interface DisplayableItemNodeVisitor { T visit(HostNode node); T visit(DataSourcesNode node); - + /* * Unsupported node */ T visit(UnsupportedContentNode ucn); - + T visit(LocalFilesDataSourceNode lfdsn); /** @@ -332,11 +330,6 @@ public interface DisplayableItemNodeVisitor { return defaultVisit(ftByMimeTypeEmptyNode); } - @Override - public T visit(InterestingHits.InterestingItemTypeNode interestingItemTypeNode) { - return defaultVisit(interestingItemTypeNode); - } - @Override public T visit(DeletedContentNode dcn) { return defaultVisit(dcn); @@ -546,12 +539,12 @@ public interface DisplayableItemNodeVisitor { public T visit(AttachmentNode node) { return defaultVisit(node); } - + @Override public T visit(OsAccounts.OsAccountNode node) { return defaultVisit(node); } - + @Override public T visit(OsAccounts.OsAccountListNode node) { return defaultVisit(node); @@ -571,12 +564,12 @@ public interface DisplayableItemNodeVisitor { public T visit(PersonNode node) { return defaultVisit(node); } - + @Override public T visit(UnsupportedContentNode node) { return defaultVisit(node); } - + @Override public T visit(LocalFilesDataSourceNode node) { return defaultVisit(node); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 4469e76356..c22d095726 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -34,8 +34,6 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -53,45 +51,44 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.datamodel.AnalysisResult; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; public class InterestingHits implements AutopsyVisitableItem { - private static final String INTERESTING_ITEMS = NbBundle - .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"); - private static final String DISPLAY_NAME = NbBundle.getMessage(InterestingHits.class, "InterestingHits.displayName.text"); private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); - private static final Map ART_TYPES = Stream.of( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, - BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT - ).collect(Collectors.toMap(tp -> tp.getTypeID(), tp -> tp, (tp1, tp2) -> tp1)); private SleuthkitCase skCase; private final InterestingResults interestingResults = new InterestingResults(); private final long filteringDSObjId; // 0 if not filtering/grouping by data source + private final BlackboardArtifact.Type artifactType; /** * Constructor * - * @param skCase Case DB + * @param skCase Case DB + * @param artifactType The artifact type (either interesting file or + * artifact). * */ - public InterestingHits(SleuthkitCase skCase) { - this(skCase, 0); + public InterestingHits(SleuthkitCase skCase, BlackboardArtifact.Type artifactType) { + this(skCase, artifactType, 0); } /** * Constructor * - * @param skCase Case DB - * @param objId Object id of the data source + * @param skCase Case DB + * @param artifactType The artifact type (either interesting file or + * artifact). + * @param objId Object id of the data source * */ - public InterestingHits(SleuthkitCase skCase, long objId) { + public InterestingHits(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, long objId) { this.skCase = skCase; + this.artifactType = artifactType; this.filteringDSObjId = objId; interestingResults.update(); } @@ -102,7 +99,7 @@ public class InterestingHits implements AutopsyVisitableItem { private class InterestingResults extends Observable { // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized - private final Map>> interestingItemsMap = new LinkedHashMap<>(); + private final Map> interestingItemsMap = new LinkedHashMap<>(); /** * Returns all the set names for a given interesting item type. @@ -111,30 +108,15 @@ public class InterestingHits implements AutopsyVisitableItem { * * @return The set names. */ - List getSetNames(BlackboardArtifact.Type type) { + List getSetNames() { List setNames; synchronized (interestingItemsMap) { - Map> setMapping = interestingItemsMap.getOrDefault(type, Collections.emptyMap()); - setNames = new ArrayList<>(setMapping.keySet()); + setNames = new ArrayList<>(interestingItemsMap.keySet()); } Collections.sort(setNames, (a, b) -> a.compareToIgnoreCase(b)); return setNames; } - /** - * Returns all types currently in the map. - * - * @return The types present in the map. - */ - List getTypes() { - List types; - synchronized (interestingItemsMap) { - types = new ArrayList<>(interestingItemsMap.keySet()); - } - Collections.sort(types, (a, b) -> a.getDisplayName().compareToIgnoreCase(b.getDisplayName())); - return types; - } - /** * Returns all artifact ids belonging to the specified interesting item * type and set name. @@ -144,9 +126,9 @@ public class InterestingHits implements AutopsyVisitableItem { * * @return The artifact ids in that set name and type. */ - Set getArtifactIds(BlackboardArtifact.Type type, String setName) { + Set getArtifactIds(String setName) { synchronized (interestingItemsMap) { - return interestingItemsMap.getOrDefault(type, Collections.emptyMap()).getOrDefault(setName, Collections.emptySet()); + return new HashSet<>(interestingItemsMap.getOrDefault(setName, Collections.emptySet())); } } @@ -174,15 +156,11 @@ public class InterestingHits implements AutopsyVisitableItem { int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); - String typeIds = ART_TYPES.keySet().stream() - .map(id -> id.toString()) - .collect(Collectors.joining(", ")); - - String query = "SELECT value_text, blackboard_artifacts.artifact_obj_id, blackboard_artifacts.artifact_type_id " //NON-NLS + String query = "SELECT value_text, blackboard_artifacts.artifact_obj_id " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS - + " AND blackboard_artifacts.artifact_type_id IN (" + typeIds + ")"; //NON-NLS + + " AND blackboard_artifacts.artifact_type_id = " + artifactType.getTypeID(); //NON-NLS if (filteringDSObjId > 0) { query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } @@ -191,12 +169,9 @@ public class InterestingHits implements AutopsyVisitableItem { synchronized (interestingItemsMap) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { - int artTypeId = resultSet.getInt("artifact_type_id"); - BlackboardArtifact.Type artType = ART_TYPES.get(artTypeId); String value = resultSet.getString("value_text"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS interestingItemsMap - .computeIfAbsent(artType, (k) -> new LinkedHashMap<>()) .computeIfAbsent(value, (k) -> new HashSet<>()) .add(artifactObjId); } @@ -212,97 +187,102 @@ public class InterestingHits implements AutopsyVisitableItem { return visitor.visit(this); } - /** - * Node for the interesting items - */ - public class RootNode extends UpdatableCountTypeNode { - - public RootNode() { - super(Children.create(new HitTypeFactory(), true), - Lookups.singleton(DISPLAY_NAME), - DISPLAY_NAME, - filteringDSObjId, - BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT); - super.setName(INTERESTING_ITEMS); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor visitor) { - return visitor.visit(this); - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "InterestingHits.createSheet.name.desc"), - getName())); - - return sheet; - } - - @Override - public String getItemType() { - return getClass().getName(); - } - } - /** * Creates nodes for all sets for a specified interesting item type. */ private class SetNameFactory extends ChildFactory.Detachable implements Observer { - private final BlackboardArtifact.Type type; - - /** - * Constructor. - * - * @param type The artifact type to filter these sets. + /* + * This should probably be in the top-level class, but the factory has + * nice methods for its startup and shutdown, so it seemed like a + * cleaner place to register the property change listener. */ - SetNameFactory(BlackboardArtifact.Type type) { - this.type = type; - } + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * Even with the check above, it is still possible that the + * case will be closed in a different thread before this + * code executes. If that happens, it is possible for the + * event to have a null oldValue. + */ + ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); + if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == artifactType.getTypeID())) { + interestingResults.update(); + } + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCaseThrows(); + interestingResults.update(); + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } + }; + + private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); @Override protected boolean createKeys(List list) { - list.addAll(interestingResults.getSetNames(this.type)); + list.addAll(interestingResults.getSetNames()); return true; } @Override protected Node createNodeForKey(String key) { - return new SetNameNode(this.type, key); - } - - @Override - protected void addNotify() { - interestingResults.update(); - interestingResults.addObserver(this); - } - - @Override - protected void finalize() throws Throwable { - interestingResults.deleteObserver(this); + return new SetNameNode(key); } @Override public void update(Observable o, Object arg) { refresh(true); } + + @Override + protected void addNotify() { + IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); + IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); + interestingResults.addObserver(this); + interestingResults.update(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + IngestManager.getInstance().removeIngestJobEventListener(weakPcl); + IngestManager.getInstance().removeIngestModuleEventListener(weakPcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); + interestingResults.deleteObserver(this); + } } /** @@ -311,20 +291,18 @@ public class InterestingHits implements AutopsyVisitableItem { public class SetNameNode extends DisplayableItemNode implements Observer { private final String setName; - private final BlackboardArtifact.Type type; - public SetNameNode(BlackboardArtifact.Type type, String setName) {//, Set children) { - super(Children.create(new HitFactory(type, setName), true), Lookups.singleton(setName)); + public SetNameNode(String setName) {//, Set children) { + super(Children.create(new HitFactory(setName), true), Lookups.singleton(setName)); this.setName = setName; - this.type = type; - super.setName(type.getDisplayName() + "_" + setName); + super.setName(setName); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS interestingResults.addObserver(this); } private void updateDisplayName() { - int sizeOfSet = interestingResults.getArtifactIds(type, setName).size(); + int sizeOfSet = interestingResults.getArtifactIds(setName).size(); super.setDisplayName(setName + " (" + sizeOfSet + ")"); } @@ -370,139 +348,28 @@ public class InterestingHits implements AutopsyVisitableItem { } } - /** - * Shows an Interesting Item type node (i.e. file hit or artifact hit). - */ - private class HitTypeFactory extends ChildFactory.Detachable implements Observer { - - /* - * This should probably be in the top-level class, but the factory has - * nice methods for its startup and shutdown, so it seemed like a - * cleaner place to register the property change listener. - */ - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCaseThrows(); - /** - * Even with the check above, it is still possible that the - * case will be closed in a different thread before this - * code executes. If that happens, it is possible for the - * event to have a null oldValue. - */ - ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); - if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() - || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())) { - interestingResults.update(); - } - } catch (NoCurrentCaseException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCaseThrows(); - interestingResults.update(); - } catch (NoCurrentCaseException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeNotify(); - skCase = null; - } - } - }; - - private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); - - private HitTypeFactory() { - super(); - interestingResults.addObserver(this); - } - - @Override - protected void addNotify() { - IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); - IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl); - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); - interestingResults.update(); - interestingResults.addObserver(this); - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - IngestManager.getInstance().removeIngestJobEventListener(weakPcl); - IngestManager.getInstance().removeIngestModuleEventListener(weakPcl); - Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); - interestingResults.deleteObserver(this); - } - - @Override - protected boolean createKeys(List list) { - list.addAll(interestingResults.getTypes()); - return true; - } - - @Override - protected Node createNodeForKey(BlackboardArtifact.Type key) { - return new InterestingItemTypeNode(key); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - } - /** * Parent node for interesting item type that shows child set nodes. */ - public class InterestingItemTypeNode extends DisplayableItemNode implements Observer { - - private final BlackboardArtifact.Type type; + public class RootNode extends UpdatableCountTypeNode { /** * Main constructor. - * - * @param type The artifact type to display. */ - private InterestingItemTypeNode(BlackboardArtifact.Type type) { - super(Children.create(new SetNameFactory(type), true), Lookups.singleton(type)); - this.type = type; + public RootNode() { + super(Children.create(new SetNameFactory(), true), + Lookups.singleton(artifactType), + artifactType.getDisplayName(), + filteringDSObjId, + artifactType); /** * We use the combination of setName and typeName as the name of the * node to ensure that nodes have a unique name. This comes into * play when associating paging state with the node. */ - super.setName(type.getDisplayName()); - updateDisplayName(); + setName(artifactType.getDisplayName()); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS - interestingResults.addObserver(this); - } - - private void updateDisplayName() { - super.setDisplayName(type.getDisplayName() + " (" + interestingResults.getSetNames(type).size() + ")"); } @Override @@ -530,11 +397,6 @@ public class InterestingHits implements AutopsyVisitableItem { return visitor.visit(this); } - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } - @Override public String getItemType() { /** @@ -550,26 +412,22 @@ public class InterestingHits implements AutopsyVisitableItem { */ private class HitFactory extends BaseChildFactory implements Observer { - private final BlackboardArtifact.Type type; private final String setName; private final Map artifactHits = new HashMap<>(); /** * Main constructor. * - * @param type The Interesting Item type of artifacts to be - * displayed. * @param setName The set name of artifacts to be displayed. */ - private HitFactory(BlackboardArtifact.Type type, String setName) { + private HitFactory(String setName) { /** * The node name passed to the parent constructor must be the same * as the name set in the InterestingItemTypeNode constructor, i.e. * setName underscore typeName */ - super(setName + "_" + type.getDisplayName()); + super(setName); this.setName = setName; - this.type = type; interestingResults.addObserver(this); } @@ -577,7 +435,7 @@ public class InterestingHits implements AutopsyVisitableItem { protected List makeKeys() { if (skCase != null) { - interestingResults.getArtifactIds(type, setName).forEach((id) -> { + interestingResults.getArtifactIds(setName).forEach((id) -> { try { if (!artifactHits.containsKey(id)) { AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 3732f3c219..6cd838b834 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1271,9 +1271,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat treeNode = getHashsetNode(typesChildren, art); } else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) { treeNode = getKeywordHitNode(typesChildren, art); - } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID() - || typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { - treeNode = getInterestingItemNode(typesChildren, art); + } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) { + treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art); + } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { + treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art); } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) { treeNode = getEmailNode(typesChildren, art); } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) { @@ -1401,16 +1402,22 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * * @param typesChildren The children object of the same category as * interesting item. + * @param artifactType The type of the artifact (interesting hit or + * artifact). * @param art The artifact. * * @return The interesting item artifact's parent node or null if cannot be * found. */ - private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) { - Node interestingItemsRootNode = typesChildren.findChild(NbBundle - .getMessage(InterestingHits.class, "InterestingHits.interestingItems.text")); - - Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); + private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art) { + Node interestingItemsRootNode = typesChildren.findChild(artifactType.getDisplayName()); + Children setNodeChildren = (interestingItemsRootNode == null) ? null : interestingItemsRootNode.getChildren(); + + // set node children for type could not be found, so return null. + if (setNodeChildren == null) { + return null; + } + String setName = null; try { setName = art.getAttributes().stream() @@ -1429,22 +1436,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat return null; } - Stream typeNodes = interestingItemsRootChildren != null ? Stream.of(interestingItemsRootChildren.getNodes(true)) : Stream.empty(); - - Children setNodeChildren = typeNodes - .filter((nd) -> { - BlackboardArtifact.Type ndType = nd.getLookup().lookup(BlackboardArtifact.Type.class); - return ndType != null && ndType.getTypeID() == art.getArtifactTypeID(); - }) - .findFirst() - .flatMap(typeNode -> Optional.ofNullable(typeNode.getChildren())) - .orElse(null); - - // set node children for type could not be found, so return null. - if (setNodeChildren == null) { - return null; - } - // make sure data is fully loaded final String finalSetName = setName; return Stream.of(setNodeChildren.getNodes(true)) From 3417b526b2848c2346549057b11283114bf1bd7c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 10 Sep 2021 14:22:48 -0400 Subject: [PATCH 6/6] fix --- .../autopsy/directorytree/DirectoryTreeTopComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 6cd838b834..70c70fe45b 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1274,7 +1274,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) { treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art); } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { - treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art); + treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, art); } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) { treeNode = getEmailNode(typesChildren, art); } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {