Merge pull request #7245 from gdicristofaro/7944-interestingItemChanges

7944 interesting item tree structure changes
This commit is contained in:
Richard Cordovano 2021-09-10 16:28:33 -04:00 committed by GitHub
commit b38a79d3e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 213 additions and 268 deletions

View File

@ -250,18 +250,16 @@ public class Artifacts {
KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode(); KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT); return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
} else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId) {
|| TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) { InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_ARTIFACT_HIT, dsObjId).new RootNode();
return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_ARTIFACT_HIT);
InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode(); } else if (TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
return new TypeNodeKey(interestingHitsNode, InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, TSK_INTERESTING_FILE_HIT, dsObjId).new RootNode();
TSK_INTERESTING_ARTIFACT_HIT, return new TypeNodeKey(interestingHitsNode, TSK_INTERESTING_FILE_HIT);
TSK_INTERESTING_FILE_HIT);
} else if (TSK_HASHSET_HIT.getTypeID() == typeId) { } else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode(); HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT); return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
} else { } else {
return new TypeNodeKey(type, dsObjId); return new TypeNodeKey(type, dsObjId);
} }
@ -278,7 +276,7 @@ public class Artifacts {
*/ */
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final Category category; private final Category category;
private final PropertyChangeListener weakPcl; private final PropertyChangeListener weakPcl;
/** /**
@ -293,7 +291,7 @@ public class Artifacts {
super(); super();
this.filteringDSObjId = filteringDSObjId; this.filteringDSObjId = filteringDSObjId;
this.category = category; this.category = category;
PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
@ -322,9 +320,9 @@ public class Artifacts {
weakPcl = WeakListeners.propertyChange(pcl, null); weakPcl = WeakListeners.propertyChange(pcl, null);
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
refreshThrottler.registerForIngestModuleEvents(); refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
@ -632,7 +630,7 @@ public class Artifacts {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
@ -643,7 +641,7 @@ public class Artifacts {
@Override @Override
protected void onRemove() { protected void onRemove() {
if(refreshThrottler != null) { if (refreshThrottler != null) {
refreshThrottler.unregisterEventListener(); refreshThrottler.unregisterEventListener();
} }
IngestManager.getInstance().removeIngestJobEventListener(weakPcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
@ -663,14 +661,14 @@ public class Artifacts {
case ANALYSIS_RESULT: case ANALYSIS_RESULT:
arts = (filteringDSObjId > 0) arts = (filteringDSObjId > 0)
? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId) ? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
: blackboard.getAnalysisResultsByType(type.getTypeID()); : blackboard.getAnalysisResultsByType(type.getTypeID());
break; break;
case DATA_ARTIFACT: case DATA_ARTIFACT:
default: default:
arts = (filteringDSObjId > 0) arts = (filteringDSObjId > 0)
? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId) ? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
: blackboard.getDataArtifacts(type.getTypeID()); : blackboard.getDataArtifacts(type.getTypeID());
break; break;
} }
@ -679,9 +677,9 @@ public class Artifacts {
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<BlackboardArtifact> toRet = (List<BlackboardArtifact>)(List<?>)arts; List<BlackboardArtifact> toRet = (List<BlackboardArtifact>) (List<?>) arts;
return toRet; return toRet;
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS

View File

@ -181,15 +181,13 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(EmptyNode.MessageNode emptyNode); T visit(EmptyNode.MessageNode emptyNode);
T visit(InterestingHits.InterestingItemTypeNode aThis);
/* /*
* Attachments * Attachments
*/ */
T visit(AttachmentNode node); T visit(AttachmentNode node);
T visit(OsAccounts.OsAccountNode node); T visit(OsAccounts.OsAccountNode node);
T visit(OsAccounts.OsAccountListNode node); T visit(OsAccounts.OsAccountListNode node);
T visit(PersonNode node); T visit(PersonNode node);
@ -197,12 +195,12 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(HostNode node); T visit(HostNode node);
T visit(DataSourcesNode node); T visit(DataSourcesNode node);
/* /*
* Unsupported node * Unsupported node
*/ */
T visit(UnsupportedContentNode ucn); T visit(UnsupportedContentNode ucn);
T visit(LocalFilesDataSourceNode lfdsn); T visit(LocalFilesDataSourceNode lfdsn);
/** /**
@ -332,11 +330,6 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(ftByMimeTypeEmptyNode); return defaultVisit(ftByMimeTypeEmptyNode);
} }
@Override
public T visit(InterestingHits.InterestingItemTypeNode interestingItemTypeNode) {
return defaultVisit(interestingItemTypeNode);
}
@Override @Override
public T visit(DeletedContentNode dcn) { public T visit(DeletedContentNode dcn) {
return defaultVisit(dcn); return defaultVisit(dcn);
@ -546,12 +539,12 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(AttachmentNode node) { public T visit(AttachmentNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(OsAccounts.OsAccountNode node) { public T visit(OsAccounts.OsAccountNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(OsAccounts.OsAccountListNode node) { public T visit(OsAccounts.OsAccountListNode node) {
return defaultVisit(node); return defaultVisit(node);
@ -571,12 +564,12 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(PersonNode node) { public T visit(PersonNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(UnsupportedContentNode node) { public T visit(UnsupportedContentNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(LocalFilesDataSourceNode node) { public T visit(LocalFilesDataSourceNode node) {
return defaultVisit(node); return defaultVisit(node);

View File

@ -51,70 +51,95 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
public class InterestingHits implements AutopsyVisitableItem { 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 Logger logger = Logger.getLogger(InterestingHits.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
private SleuthkitCase skCase; private SleuthkitCase skCase;
private final InterestingResults interestingResults = new InterestingResults(); private final InterestingResults interestingResults = new InterestingResults();
private final long filteringDSObjId; // 0 if not filtering/grouping by data source private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private final BlackboardArtifact.Type artifactType;
/** /**
* Constructor * Constructor
* *
* @param skCase Case DB * @param skCase Case DB
* @param artifactType The artifact type (either interesting file or
* artifact).
* *
*/ */
public InterestingHits(SleuthkitCase skCase) { public InterestingHits(SleuthkitCase skCase, BlackboardArtifact.Type artifactType) {
this(skCase, 0); this(skCase, artifactType, 0);
} }
/** /**
* Constructor * Constructor
* *
* @param skCase Case DB * @param skCase Case DB
* @param objId Object id of the data source * @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.skCase = skCase;
this.artifactType = artifactType;
this.filteringDSObjId = objId; this.filteringDSObjId = objId;
interestingResults.update(); interestingResults.update();
} }
/**
* Cache of result ids mapped by artifact type -> set name -> artifact id.
*/
private class InterestingResults extends Observable { private class InterestingResults extends Observable {
// NOTE: the map can be accessed by multiple worker threads and needs to be synchronized // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
private final Map<String, Map<String, Set<Long>>> interestingItemsMap = new LinkedHashMap<>(); private final Map<String, Set<Long>> interestingItemsMap = new LinkedHashMap<>();
public List<String> getSetNames() { /**
* Returns all the set names for a given interesting item type.
*
* @param type The interesting item type.
*
* @return The set names.
*/
List<String> getSetNames() {
List<String> setNames; List<String> setNames;
synchronized (interestingItemsMap) { synchronized (interestingItemsMap) {
setNames = new ArrayList<>(interestingItemsMap.keySet()); setNames = new ArrayList<>(interestingItemsMap.keySet());
} }
Collections.sort(setNames); Collections.sort(setNames, (a, b) -> a.compareToIgnoreCase(b));
return setNames; return setNames;
} }
public Set<Long> getArtifactIds(String setName, String typeName) { /**
* 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<Long> getArtifactIds(String setName) {
synchronized (interestingItemsMap) { synchronized (interestingItemsMap) {
return interestingItemsMap.get(setName).get(typeName); return new HashSet<>(interestingItemsMap.getOrDefault(setName, Collections.emptySet()));
} }
} }
public void update() { /**
* Triggers a fetch from the database to update this cache.
*/
void update() {
synchronized (interestingItemsMap) { synchronized (interestingItemsMap) {
interestingItemsMap.clear(); interestingItemsMap.clear();
} }
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT); loadArtifacts();
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT);
setChanged(); setChanged();
notifyObservers(); notifyObservers();
} }
@ -124,18 +149,18 @@ public class InterestingHits implements AutopsyVisitableItem {
* the interestingItemsMap * the interestingItemsMap
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void loadArtifacts(BlackboardArtifact.Type artType) { private void loadArtifacts() {
if (skCase == null) { if (skCase == null) {
return; return;
} }
int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); 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 query = "SELECT value_text, blackboard_artifacts.artifact_obj_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + setNameId //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //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 = " + artifactType.getTypeID(); //NON-NLS
if (filteringDSObjId > 0) { if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
} }
@ -146,12 +171,9 @@ public class InterestingHits implements AutopsyVisitableItem {
while (resultSet.next()) { while (resultSet.next()) {
String value = resultSet.getString("value_text"); //NON-NLS String value = resultSet.getString("value_text"); //NON-NLS
long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
if (!interestingItemsMap.containsKey(value)) { interestingItemsMap
interestingItemsMap.put(value, new LinkedHashMap<>()); .computeIfAbsent(value, (k) -> new HashSet<>())
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>()); .add(artifactObjId);
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
}
interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactObjId);
} }
} }
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
@ -166,54 +188,8 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
/** /**
* Node for the interesting items * Creates nodes for all sets for a specified interesting item type.
*/ */
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new SetNameFactory(), 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> T accept(DisplayableItemNodeVisitor<T> 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();
}
}
private class SetNameFactory extends ChildFactory.Detachable<String> implements Observer { private class SetNameFactory extends ChildFactory.Detachable<String> implements Observer {
/* /*
@ -239,8 +215,7 @@ public class InterestingHits implements AutopsyVisitableItem {
* event to have a null oldValue. * event to have a null oldValue.
*/ */
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == artifactType.getTypeID())) {
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())) {
interestingResults.update(); interestingResults.update();
} }
} catch (NoCurrentCaseException notUsed) { } catch (NoCurrentCaseException notUsed) {
@ -272,26 +247,8 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@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 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
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 @Override
protected boolean createKeys(List<String> list) { protected boolean createKeys(List<String> list) {
@ -308,14 +265,35 @@ public class InterestingHits implements AutopsyVisitableItem {
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
refresh(true); 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);
}
} }
/**
* A node for a set to be displayed in the tree.
*/
public class SetNameNode extends DisplayableItemNode implements Observer { public class SetNameNode extends DisplayableItemNode implements Observer {
private final String setName; private final String setName;
public SetNameNode(String setName) {//, Set<Long> children) { public SetNameNode(String setName) {//, Set<Long> children) {
super(Children.create(new HitTypeFactory(setName), true), Lookups.singleton(setName)); super(Children.create(new HitFactory(setName), true), Lookups.singleton(setName));
this.setName = setName; this.setName = setName;
super.setName(setName); super.setName(setName);
updateDisplayName(); updateDisplayName();
@ -324,111 +302,81 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
private void updateDisplayName() { private void updateDisplayName() {
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size() int sizeOfSet = interestingResults.getArtifactIds(setName).size();
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
super.setDisplayName(setName + " (" + sizeOfSet + ")"); super.setDisplayName(setName + " (" + sizeOfSet + ")");
} }
@Override
public boolean isLeafTypeNode() {
return true;
}
@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> T accept(DisplayableItemNodeVisitor<T> 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();
}
}
/**
* Parent node for interesting item type that shows child set nodes.
*/
public class RootNode extends UpdatableCountTypeNode {
/**
* Main constructor.
*/
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.
*/
setName(artifactType.getDisplayName());
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
}
@Override @Override
public boolean isLeafTypeNode() { public boolean isLeafTypeNode() {
return false; 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> T accept(DisplayableItemNodeVisitor<T> 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<String> implements Observer {
private final String setName;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
private HitTypeFactory(String setName) {
super();
this.setName = setName;
interestingResults.addObserver(this);
}
@Override
protected boolean createKeys(List<String> 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);
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
}
}
public class InterestingItemTypeNode extends DisplayableItemNode implements Observer {
private final String typeName;
private final String setName;
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);
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() + ")");
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = super.createSheet(); Sheet sheet = super.createSheet();
@ -449,11 +397,6 @@ public class InterestingHits implements AutopsyVisitableItem {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public void update(Observable o, Object arg) {
updateDisplayName();
}
@Override @Override
public String getItemType() { public String getItemType() {
/** /**
@ -464,21 +407,27 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
} }
/**
* Factory for creating individual interesting item BlackboardArtifactNodes.
*/
private class HitFactory extends BaseChildFactory<AnalysisResult> implements Observer { private class HitFactory extends BaseChildFactory<AnalysisResult> implements Observer {
private final String setName; private final String setName;
private final String typeName;
private final Map<Long, AnalysisResult> artifactHits = new HashMap<>(); private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
private HitFactory(String setName, String typeName) { /**
* Main constructor.
*
* @param setName The set name of artifacts to be displayed.
*/
private HitFactory(String setName) {
/** /**
* The node name passed to the parent constructor must be the same * The node name passed to the parent constructor must be the same
* as the name set in the InterestingItemTypeNode constructor, i.e. * as the name set in the InterestingItemTypeNode constructor, i.e.
* setName underscore typeName * setName underscore typeName
*/ */
super(setName + "_" + typeName); super(setName);
this.setName = setName; this.setName = setName;
this.typeName = typeName;
interestingResults.addObserver(this); interestingResults.addObserver(this);
} }
@ -486,7 +435,7 @@ public class InterestingHits implements AutopsyVisitableItem {
protected List<AnalysisResult> makeKeys() { protected List<AnalysisResult> makeKeys() {
if (skCase != null) { if (skCase != null) {
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { interestingResults.getArtifactIds(setName).forEach((id) -> {
try { try {
if (!artifactHits.containsKey(id)) { if (!artifactHits.containsKey(id)) {
AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id); AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);

View File

@ -1271,9 +1271,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
treeNode = getHashsetNode(typesChildren, art); treeNode = getHashsetNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) { } else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
treeNode = getKeywordHitNode(typesChildren, art); treeNode = getKeywordHitNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID() } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()) {
|| typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, art);
treeNode = getInterestingItemNode(typesChildren, art); } else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
treeNode = getInterestingItemNode(typesChildren, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, art);
} else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) { } else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
treeNode = getEmailNode(typesChildren, art); treeNode = getEmailNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) { } else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {
@ -1401,42 +1402,46 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* *
* @param typesChildren The children object of the same category as * @param typesChildren The children object of the same category as
* interesting item. * interesting item.
* @param artifactType The type of the artifact (interesting hit or
* artifact).
* @param art The artifact. * @param art The artifact.
* *
* @return The interesting item artifact's parent node or null if cannot be * @return The interesting item artifact's parent node or null if cannot be
* found. * found.
*/ */
private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) { private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact.Type artifactType, BlackboardArtifact art) {
Node interestingItemsRootNode = typesChildren.findChild(NbBundle Node interestingItemsRootNode = typesChildren.findChild(artifactType.getDisplayName());
.getMessage(InterestingHits.class, "InterestingHits.interestingItems.text")); Children setNodeChildren = (interestingItemsRootNode == null) ? null : interestingItemsRootNode.getChildren();
Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
// set node children for type could not be found, so return null.
if (setNodeChildren == null) {
return null;
}
String setName = null;
try { try {
String setName = null; setName = art.getAttributes().stream()
List<BlackboardAttribute> attributes = art.getAttributes(); .filter(attr -> attr.getAttributeType().getTypeID() == BlackboardAttribute.Type.TSK_SET_NAME.getTypeID())
for (BlackboardAttribute att : attributes) { .map(attr -> attr.getValueString())
int typeId = att.getAttributeType().getTypeID(); .findFirst()
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { .orElse(null);
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];
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null; return null;
} }
// if no set name, no set node will be identified.
if (setName == null) {
return null;
}
// make sure data is fully loaded
final String finalSetName = setName;
return Stream.of(setNodeChildren.getNodes(true))
.filter(setNode -> finalSetName.equals(setNode.getLookup().lookup(String.class)))
.findFirst()
.orElse(null);
} }
/** /**