counts in special artifact nodes

This commit is contained in:
Greg DiCristofaro 2021-04-30 11:01:09 -04:00
parent e2f83a897a
commit eadb3a8c6b
6 changed files with 125 additions and 84 deletions

View File

@ -32,7 +32,6 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -45,10 +44,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
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.ExtractedContent.UpdatableTypeCountNode;
/**
* Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree.
@ -59,7 +60,6 @@ import org.sleuthkit.datamodel.TskCoreException;
public class EmailExtracted implements AutopsyVisitableItem {
private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
@ -92,14 +92,6 @@ public class EmailExtracted implements AutopsyVisitableItem {
private final EmailResults emailResults;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Returns the display name for this module.
* @return The display name for this module.
*/
static String getDisplayName() {
return DISPLAY_NAME;
}
/**
* Constructor
*
@ -213,12 +205,16 @@ public class EmailExtracted implements AutopsyVisitableItem {
* Mail root node grouping all mail accounts, supports account-> folder
* structure
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableTypeCountNode {
public RootNode() {
super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new AccountFactory(), true),
Lookups.singleton(TSK_EMAIL_MSG.getDisplayName()),
TSK_EMAIL_MSG.getDisplayName(),
filteringDSObjId,
new BlackboardArtifact.Type(TSK_EMAIL_MSG));
//super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super.setName(LABEL_NAME);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
emailResults.update();
}

View File

@ -20,9 +20,11 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@ -32,6 +34,7 @@ import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
@ -185,12 +188,14 @@ public class ExtractedContent implements AutopsyVisitableItem {
}
private static class AnalysisResultsTypeFactory extends TypeFactory {
AnalysisResultsTypeFactory(long filteringDSObjId) {
super(Category.ANALYSIS_RESULT, filteringDSObjId);
}
}
private static class DataArtifactsTypeFactory extends TypeFactory {
DataArtifactsTypeFactory(long filteringDSObjId) {
super(Category.DATA_ARTIFACT, filteringDSObjId);
}
@ -274,7 +279,9 @@ public class ExtractedContent implements AutopsyVisitableItem {
null,
Sets.newHashSet(new BlackboardArtifact.Type(TSK_KEYWORD_HIT)));
} else if (TSK_EMAIL_MSG.getTypeID() == typeId) {
} 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 TypeNodeRecord(
interestingHitsNode,
@ -351,12 +358,10 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected boolean createKeys(List<TypeNodeRecord> list) {
try {
// Potentially can reuse
List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
: Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse();
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
: skCase.getArtifactTypesInUse();
List<TypeNodeRecord> allKeysSorted = types.stream()
.filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
@ -435,41 +440,84 @@ public class ExtractedContent implements AutopsyVisitableItem {
}
}
public static abstract class UpdatableTypeCountNode extends DisplayableItemNode {
private static final Logger logger = Logger.getLogger(UpdatableTypeCountNode.class.getName());
private final Set<BlackboardArtifact.Type> types;
private final long filteringDSObjId;
private long childCount = 0;
private final String baseName;
/**
* Constructs a node that is eligible for display in the tree view or
* results view. Capabilitites include accepting a
* DisplayableItemNodeVisitor, indicating whether or not the node is a
* leaf node, providing an item type string suitable for use as a key,
* and storing information about a child node that is to be selected if
* the node is selected in the tree view.
*
* @param children The Children object for the node.
* @param lookup The Lookup object for the node.
*/
public UpdatableTypeCountNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types) {
super(children, lookup);
this.types = Stream.of(types).collect(Collectors.toSet());
this.filteringDSObjId = filteringDSObjId;
this.baseName = baseName;
updateDisplayName();
}
protected long getChildCount() {
return this.childCount;
}
void updateDisplayName() {
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
int count = 0;
for (BlackboardArtifact.Type type : this.types) {
if (filteringDSObjId > 0) {
count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
} else {
count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
}
}
this.childCount = count;
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
}
}
/**
* Node encapsulating blackboard artifact type. This is used on the
* left-hand navigation side of the Autopsy UI as the parent node for all of
* the artifacts of a given type. Its children will be
* BlackboardArtifactNode objects.
*/
public static class TypeNode extends DisplayableItemNode {
public static class TypeNode extends UpdatableTypeCountNode {
private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
private final BlackboardArtifact.Type type;
private long childCount = 0;
private final long filteringDSObjId;
TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
super(Children.create(new ArtifactFactory(type, filteringDSObjId), true), Lookups.singleton(type.getDisplayName()));
super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
Lookups.singleton(type.getDisplayName()),
type.getDisplayName(),
filteringDSObjId,
type);
super.setName(type.getTypeName());
this.type = type;
this.filteringDSObjId = filteringDSObjId;
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
updateDisplayName();
}
final void updateDisplayName() {
try {
this.childCount = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId)
: Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifactsTypeCount(type.getTypeID());
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(type.getDisplayName() + " \u200E(\u200E" + childCount + ")\u200E");
}
@Override
@ -489,7 +537,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
childCount));
getChildCount()));
return sheet;
}

View File

@ -47,10 +47,14 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode;
/**
* Hash set hits node support. Inner classes have all of the nodes in the tree.
@ -66,13 +70,6 @@ public class HashsetHits implements AutopsyVisitableItem {
private final HashsetResults hashsetResults;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Returns the display name for this module.
* @return The display name for this module.
*/
static String getDisplayName() {
return DISPLAY_NAME;
}
/**
* Constructor
@ -176,10 +173,15 @@ public class HashsetHits implements AutopsyVisitableItem {
/**
* Top-level node for all hash sets
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableTypeCountNode {
public RootNode() {
super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new HashsetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
new BlackboardArtifact.Type(TSK_HASHSET_HIT));
super.setName(HASHSET_HITS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS

View File

@ -50,6 +50,9 @@ 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.ExtractedContent.UpdatableTypeCountNode;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
public class InterestingHits implements AutopsyVisitableItem {
@ -63,14 +66,6 @@ public class InterestingHits implements AutopsyVisitableItem {
private final InterestingResults interestingResults = new InterestingResults();
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Returns the display name for this module.
* @return The display name for this module.
*/
static String getDisplayName() {
return DISPLAY_NAME;
}
/**
* Constructor
*
@ -173,12 +168,16 @@ public class InterestingHits implements AutopsyVisitableItem {
/**
* Node for the interesting items
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableTypeCountNode {
public RootNode() {
super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new SetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT),
new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
super.setName(INTERESTING_ITEMS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
}

View File

@ -55,6 +55,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode;
/**
* Keyword hits node support
@ -104,15 +106,6 @@ public class KeywordHits implements AutopsyVisitableItem {
return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
}
/**
* Returns the display name for KeywordHits.
* @return The display name for KeywordHits.
*/
static String getDisplayName() {
return KEYWORD_HITS;
}
/**
* Constructor
*
@ -384,12 +377,16 @@ public class KeywordHits implements AutopsyVisitableItem {
}
// Created by CreateAutopsyNodeVisitor
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableTypeCountNode {
public RootNode() {
super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
super(Children.create(new ListFactory(), true),
Lookups.singleton(KEYWORD_HITS),
KEYWORD_HITS,
filteringDSObjId,
new BlackboardArtifact.Type(TSK_KEYWORD_HIT));
super.setName(NAME);
super.setDisplayName(KEYWORD_HITS);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
}

View File

@ -69,6 +69,8 @@ import org.sleuthkit.autopsy.datamodel.CreditCards;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.ExtractedContent;
import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -77,6 +79,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
@ -115,14 +118,6 @@ final public class Accounts implements AutopsyVisitableItem {
private final AccountTypeResults accountTypeResults;
/**
* Returns the display name for this module.
* @return The display name for this module.
*/
public static String getDisplayName() {
return DISPLAY_NAME;
}
/**
* Constructor
*
@ -241,12 +236,16 @@ final public class Accounts implements AutopsyVisitableItem {
* Top-level node for the accounts tree
*/
@NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
final public class AccountsRootNode extends DisplayableItemNode {
final public class AccountsRootNode extends UpdatableTypeCountNode {
public AccountsRootNode() {
super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
super(Children.create(new AccountTypeFactory(), true),
Lookups.singleton(Accounts.this),
DISPLAY_NAME,
filteringDSObjId,
new BlackboardArtifact.Type(TSK_ACCOUNT));
setName(Accounts.NAME);
setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
}
@ -303,7 +302,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ " GROUP BY blackboard_attributes.value_text ";