Tidy up BlackboardArtifactNode.java

This commit is contained in:
Richard Cordovano 2020-03-14 10:53:24 -04:00
parent fccf6b53cd
commit a316eda337

View File

@ -93,20 +93,16 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build(); private static final Cache<Long, Content> contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
/* /*
* Case application events that affect the property sheets of * Case events that indicate an update to the node's property sheet may be
* BlackboardArtifactNodes. * required.
*
* NOTE: CURRENT_CASE (closed) events trigger content cache invalidation and
* unregistering of the PropertyChangeListener subscribed to the application
* events.
*/ */
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of( private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_ADDED,
Case.Events.CONTENT_TAG_DELETED, Case.Events.CONTENT_TAG_DELETED,
Case.Events.CURRENT_CASE, Case.Events.CR_COMMENT_CHANGED,
Case.Events.CR_COMMENT_CHANGED); Case.Events.CURRENT_CASE);
/* /*
* Artifact types for which the unique path of the artifact's source content * Artifact types for which the unique path of the artifact's source content
@ -120,20 +116,23 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}; };
/* /*
* Artifact types for which the file metadata of the artifact's source * Artifact types for which the file metadata of the artifact's source file
* content (a file) should be displayed in the node's property sheet. * should be displayed in the node's property sheet.
*/ */
private static final Integer[] SHOW_FILE_METADATA = new Integer[]{ private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
}; };
private final BlackboardArtifact artifact; private final BlackboardArtifact artifact;
private Content sourceContent; private Content srcContent; // May be null.
private List<NodeProperty<? extends Object>> customProperties;
/* /*
* The node's application event listener. * A method has been provided to allow the injection of properties into this
* node for display in the node's property sheet, independent of the
* artifact the node represents.
*/ */
private List<NodeProperty<? extends Object>> customProperties;
private final PropertyChangeListener appEventListener = new PropertyChangeListener() { private final PropertyChangeListener appEventListener = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
@ -149,19 +148,25 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
updateSheet(); updateSheet();
} }
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt; if (srcContent != null) {
if (event.getAddedTag().getContent().equals(sourceContent)) { ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
updateSheet(); if (event.getAddedTag().getContent().equals(srcContent)) {
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; if (srcContent != null) {
if (event.getDeletedTagInfo().getContentID() == sourceContent.getId()) { ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
updateSheet(); if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
CommentChangedEvent event = (CommentChangedEvent) evt; if (srcContent != null) {
if (event.getContentID() == sourceContent.getId()) { CommentChangedEvent event = (CommentChangedEvent) evt;
updateSheet(); if (event.getContentID() == srcContent.getId()) {
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) { if (evt.getNewValue() == null) {
@ -187,14 +192,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}; };
/* /*
* The node's application event listener is wrapped in a weak reference that * The node's event listener is wrapped in a weak reference that allows the
* allows the node to be garbage collected when the NetBeans infrastructure * node to be garbage collected when the NetBeans infrastructure discards
* discards it because the user has navigated to another part of the tree. * it. If this is not done, it has been shown that strong references to the
* If this is not done, the strong reference to the listener held by the the * listener held by event publishers prevents garbage collection of this
* event publisher prevents garbage collection of this node. * node.
*
* RC: Isn't there some node lifecycle property change event that could be
* used to unregister the listener?
*/ */
private final PropertyChangeListener weakAppEventListener = WeakListeners.propertyChange(appEventListener, null); private final PropertyChangeListener weakAppEventListener = WeakListeners.propertyChange(appEventListener, null);
@ -208,24 +210,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) { public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact)); super(artifact, createLookup(artifact));
this.artifact = artifact; this.artifact = artifact;
/*
* RC: NPE ALERT!! The createLookup method can return a Lookup without
* the source content in it, so this loop has the potential to leave
* this.sourceContent null, causing NPEs within this class and in the UI
* component clients of this class. This constructor should throw
* instead. However, that would be an exported public API change.
*/
for (Content lookupContent : this.getLookup().lookupAll(Content.class)) { for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) { if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
sourceContent = lookupContent; srcContent = lookupContent;
try { try {
/* /*
* Get the unique path of the source content now (it is * Calling this getter causes the unique path of the source
* cached in the Content object). * content to be cached in the Content object. This is
* advantageous if this node is constructed in a background
* thread instead of a UI thread.
*/ */
this.sourceContent.getUniquePath(); srcContent.getUniquePath();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); logger.log(Level.SEVERE, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
} }
break; break;
} }
@ -247,8 +244,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
/** /**
* Creates a Lookup object for this node and populates it with the artifact * Creates a Lookup object for this node and populates it with both the
* this node represents and its source content. * artifact this node represents and its source content.
* *
* @param artifact The artifact this node represents. * @param artifact The artifact this node represents.
* *
@ -264,24 +261,62 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
return Lookups.fixed(artifact, content); return Lookups.fixed(artifact, content);
} }
} catch (ExecutionException ex) { } catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}, content objID={1})", artifact.getId(), objectID), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
/*
* RC: NPE ALERT!! The decision to return a Lookup without the
* source content in it has the potential to cause NPEs within this
* class and in the UI component clients of this class. Thsi method
* should throw and the node constructor should propagate the
* exception. However, that would be an exported public API change.
*/
return Lookups.fixed(artifact); return Lookups.fixed(artifact);
} }
} }
/**
* Sets the display name for this node.
*/
@NbBundle.Messages({"# {0} - artifactDisplayName", "BlackboardArtifactNode.displayName.artifact={0} Artifact"})
private void setDisplayName() {
String displayName = ""; //NON-NLS
/*
* If the artifact this node represents is a keyword hit artifact and
* the associated artifact can be retreived from the case database, make
* the display name the display name of the associated artifact.
*
* If the artifact this node represents is an interesting artifact hit
* and the associated artifact can be retreived from the case database,
* make the display name the display name of the artifact this node
* represents.
*/
if (artifact != null
&& (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
|| artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID())) {
try {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
if (associatedArtifact != null) {
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
displayName = Bundle.BlackboardArtifactNode_displayName_artifact(artifact.getDisplayName());
} else {
displayName = Bundle.BlackboardArtifactNode_displayName_artifact(associatedArtifact.getDisplayName());
}
}
}
}
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_KEYWORD_HIT/TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
}
}
if (displayName.isEmpty() && artifact != null) {
displayName = artifact.getDisplayName();
}
setDisplayName(displayName);
}
/** /**
* Unregisters the application event listener when this node is garbage * Unregisters the application event listener when this node is garbage
* collected, if this finalizer is called. * collected, if this finalizer is actually called.
* *
* RC: Isn't there some node lifecycle property change event that could be * RC: Isn't there some node lifecycle property change event that could be
* used to unregister the listener? * used to unregister the listener instead?
* *
* @throws Throwable * @throws Throwable
*/ */
@ -321,7 +356,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
actionsList.add(new ViewArtifactInTimelineAction(artifact)); actionsList.add(new ViewArtifactInTimelineAction(artifact));
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting attributes of artifact (onbjID={0})", artifact.getArtifactID()), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
} }
/* /*
@ -335,7 +370,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile)); actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (onbjID={0})", artifact.getArtifactID()), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
} }
/* /*
@ -350,58 +385,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
/**
* Sets the display name for this node.
*
* RC: I am not sure that the naming algorithm in this complicated and
* potentially fairly costly method has any value whatsoever. This should be
* investigated. In the mean time, I am preserving all of the comments I
* found here including: "Set the filter node display name. The value will
* either be the file name or something along the lines of e.g. "Messages
* Artifact" for keyword hits on artifacts." Note that this method swallows
* exceptions without logging them and also has a local variable that hides
* a superclass field with inappropriate package private access.
*/
@NbBundle.Messages({"# {0} - artifactDisplayName", "BlackboardArtifactNode.displayName.artifact={0} Artifact"})
private void setDisplayName() {
String displayName = ""; //NON-NLS
// If this is a node for a keyword hit on an artifact, we set the
// display name to be the artifact type name followed by " Artifact"
// e.g. "Messages Artifact".
if (artifact != null
&& (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
|| artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID())) {
try {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
if (associatedArtifact != null) {
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
artifact.getDisplayName();
} else {
displayName = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.displayName.artifact", associatedArtifact.getDisplayName());
}
}
}
}
} catch (TskCoreException | NoCurrentCaseException ex) {
// Do nothing since the display name will be set to the file name.
}
}
if (displayName.isEmpty() && artifact != null) {
try {
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID());
displayName = (content == null) ? artifact.getName() : content.getName();
} catch (TskCoreException | NoCurrentCaseException ex) {
displayName = artifact.getName();
}
}
this.setDisplayName(displayName);
}
/** /**
* Gets the name of the source content of the artifact represented by this * Gets the name of the source content of the artifact represented by this
* node. * node.
@ -409,7 +392,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @return The source content name. * @return The source content name.
*/ */
public String getSourceName() { public String getSourceName() {
return sourceContent.getName(); String name = "";
if (srcContent != null) {
srcContent.getName();
}
return name;
} }
@NbBundle.Messages({ @NbBundle.Messages({
@ -516,8 +503,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) { if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
String ext = ""; //NON-NLS String ext = ""; //NON-NLS
String actualMimeType = ""; //NON-NLS String actualMimeType = ""; //NON-NLS
if (sourceContent instanceof AbstractFile) { if (srcContent != null && srcContent instanceof AbstractFile) {
AbstractFile file = (AbstractFile) sourceContent; AbstractFile file = (AbstractFile) srcContent;
ext = file.getNameExtension(); ext = file.getNameExtension();
actualMimeType = file.getMIMEType(); actualMimeType = file.getMIMEType();
if (actualMimeType == null) { if (actualMimeType == null) {
@ -541,10 +528,12 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) { if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
String sourcePath = ""; //NON-NLS String sourcePath = ""; //NON-NLS
try { if (srcContent != null) {
sourcePath = sourceContent.getUniquePath(); try {
} catch (TskCoreException ex) { sourcePath = srcContent.getUniquePath();
logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); //NON-NLS } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
}
} }
if (sourcePath.isEmpty() == false) { if (sourcePath.isEmpty() == false) {
@ -561,7 +550,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* sheet. Otherwise, add the data source to the sheet. * sheet. Otherwise, add the data source to the sheet.
*/ */
if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) { if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
AbstractFile file = sourceContent instanceof AbstractFile ? (AbstractFile) sourceContent : null; AbstractFile file = srcContent != null && srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"), sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
@ -581,7 +570,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"), sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
"", "",
sourceContent.getSize())); file == null ? "" : file.getSize()));
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(), sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(), Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
"", "",
@ -589,15 +578,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
} else { } else {
String dataSourceStr = ""; String dataSourceStr = "";
try { if (srcContent != null) {
Content dataSource = sourceContent.getDataSource(); try {
if (dataSource != null) { Content dataSource = srcContent.getDataSource();
dataSourceStr = dataSource.getName(); if (dataSource != null) {
} else { dataSourceStr = dataSource.getName();
dataSourceStr = getRootAncestorName(); } else {
dataSourceStr = getRootAncestorName();
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
} }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source data soiurce name (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); //NON-NLS
} }
if (dataSourceStr.isEmpty() == false) { if (dataSourceStr.isEmpty() == false) {
@ -616,8 +607,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) { if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
long size = 0; long size = 0;
String path = ""; //NON-NLS String path = ""; //NON-NLS
if (sourceContent instanceof AbstractFile) { if (srcContent != null && srcContent instanceof AbstractFile) {
AbstractFile af = (AbstractFile) sourceContent; AbstractFile af = (AbstractFile) srcContent;
size = af.getSize(); size = af.getSize();
try { try {
path = af.getUniquePath(); path = af.getUniquePath();
@ -650,9 +641,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
List<Tag> tags = new ArrayList<>(); List<Tag> tags = new ArrayList<>();
try { try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(sourceContent)); if (srcContent != null) {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
} }
return tags; return tags;
} }
@ -668,28 +661,34 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
@Override @Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null; CorrelationAttributeInstance correlationAttribute = null;
if (CentralRepository.isEnabled() && sourceContent instanceof AbstractFile) { if (srcContent != null && CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) sourceContent); correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
} }
return correlationAttribute; return correlationAttribute;
} }
/** /**
* Computes the value of the comment property ("C" in S, C, O) for the * Computes the value of the comment property ("C" in S, C, O) for the
* artifact represented by this node. An icon is displayed if a commented * artifact represented by this node.
* tag has been applied to the artifact or its source content, or if there *
* is a comment on the MD5 hash of the source file in the central * An icon is displayed in the property sheet if a commented tag has been
* applied to the artifact or its source content, or if there is a
* corresponding commented correlation attribute instance in the central
* repository. * repository.
* *
* @param tags The tags applied to the artifact and its source content. * @param tags The tags applied to the artifact and its source content.
* @param attribute The correlation attribute for the MD5 hash of the source * @param attribute A correlation attribute instance Ffor the central
* file of the artifact, may be null. * repository lookup.
* *
* @return The value of the comment property. * @return The value of the comment property.
*/ */
@Override @Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) { protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
/*
* Has a tag with a comment been applied to the artifact or its source
* content?
*/
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT; HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (Tag tag : tags) { for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) { if (!StringUtils.isBlank(tag.getComment())) {
@ -698,6 +697,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
} }
/*
* Does the given correlation attribute instance have a comment in the
* central repository?
*/
if (attribute != null && !StringUtils.isBlank(attribute.getComment())) { if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
if (status == HasCommentStatus.TAG_COMMENT) { if (status == HasCommentStatus.TAG_COMMENT) {
status = HasCommentStatus.CR_AND_TAG_COMMENTS; status = HasCommentStatus.CR_AND_TAG_COMMENTS;
@ -712,12 +715,16 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
/** /**
* Computes the value of the score property ("S" in S, C, O) for the * Computes the value of the score property ("S" in S, C, O) for the
* artifact represented by this node. The score property indicates whether * artifact represented by this node. The score property indicates whether
* the artifact or its source content is interesting or notable. A red icon * the artifact or its source content is notable or interesting.
* will be displayed if the hash of the source file has been found in a *
* notable hash set or if either the artifact or its source content has been * IMPORTANT: Notability takes precedence when computing the score.
* tagged with a notable tag. A yellow icon will be displayed if the source *
* file belongs to an interesting file set or either the artifact or its * A red icon will be displayed in the property sheet if the hash of the
* source content has been tagged with a non-notable tag. * source file has been found in a notable hash set or if either the
* artifact or its source content has been tagged with a notable tag. A
* yellow icon will be displayed if the source file belongs to an
* interesting file set or either the artifact or its source content has
* been tagged with a non-notable tag.
* *
* @param tags The tags that have been applied to the artifact and its * @param tags The tags that have been applied to the artifact and its
* source content. * source content.
@ -727,18 +734,25 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
@Override @Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) { protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
/*
* Is the artifact's source content marked as notable?
*/
Score score = Score.NO_SCORE; Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description(); String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (sourceContent instanceof AbstractFile) { if (srcContent != null && srcContent instanceof AbstractFile) {
if (((AbstractFile) sourceContent).getKnown() == TskData.FileKnown.BAD) { if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE; score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description(); description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
} }
} }
//if the artifact being viewed is a hashhit check if the hashset is notable
if ((score == Score.NO_SCORE || score == Score.INTERESTING_SCORE) && content.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { /*
* If the artifact is a hash set hit, is the hash set a notable hashes
* hash set?
*/
if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
try { try {
BlackboardAttribute attr = content.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME)); BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
List<HashDbManager.HashDb> notableHashsets = HashDbManager.getInstance().getKnownBadFileHashSets(); List<HashDbManager.HashDb> notableHashsets = HashDbManager.getInstance().getKnownBadFileHashSets();
for (HashDbManager.HashDb hashDb : notableHashsets) { for (HashDbManager.HashDb hashDb : notableHashsets) {
if (hashDb.getHashSetName().equals(attr.getValueString())) { if (hashDb.getHashSetName().equals(attr.getValueString())) {
@ -748,18 +762,29 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
//unable to get the attribute so we can not update the status based on the attribute logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute for TSK_HASHSET_HIT artifact (artifact objID={0})", artifact.getId()), ex);
logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex);
} }
} }
try {
if (score == Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) { /*
score = Score.INTERESTING_SCORE; * Is the artifact's source content notable?
description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description(); */
if (score == Score.NO_SCORE && srcContent != null) {
try {
if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
score = Score.INTERESTING_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description();
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts for source content (artifact objID={0})", artifact.getId()), ex);
} }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex);
} }
/*
* Analyze any tags applied to the artifact or its source content. If
* there are tags, tha artifact is at least interesting. If one of the
* tags is a notable tag, the artifact is notable.
*/
if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) { if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
score = Score.INTERESTING_SCORE; score = Score.INTERESTING_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description(); description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
@ -798,7 +823,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
Long count = -1L; Long count = -1L;
String description = defaultDescription; String description = defaultDescription;
try { try {
//don't perform the query if there is no correlation value
if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) { if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) {
count = CentralRepository.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(corrAttrType, attributeValue); count = CentralRepository.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(corrAttrType, attributeValue);
description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName()); description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName());
@ -806,9 +830,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description(); description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
} }
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, content objID={1}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), sourceContent.getId(), corrAttrType, attributeValue), ex); logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), corrAttrType, attributeValue), ex);
} catch (CorrelationAttributeNormalizationException ex) { } catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, content objID={1}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), sourceContent.getId(), corrAttrType, attributeValue), ex); logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), corrAttrType, attributeValue), ex);
} }
return Pair.of(count, description); return Pair.of(count, description);
} }
@ -827,21 +851,22 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @return The root ancestor name or the empty string if an error occurs. * @return The root ancestor name or the empty string if an error occurs.
*/ */
private String getRootAncestorName() { private String getRootAncestorName() {
String parentName = sourceContent.getName(); String parentName = srcContent.getName();
Content parent = sourceContent; Content parent = srcContent;
try { try {
while ((parent = parent.getParent()) != null) { while ((parent = parent.getParent()) != null) {
parentName = parent.getName(); parentName = parent.getName();
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId())); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId())); //NON-NLS
return ""; return "";
} }
return parentName; return parentName;
} }
/** /**
* Adds a "custom" property to the property sheet of this node. * Adds a "custom" property to the property sheet of this node, indepoendent
* of the artifact this node represents or its source content.
* *
* @param property The custom property. * @param property The custom property.
*/ */
@ -865,7 +890,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
try { try {
for (BlackboardAttribute attribute : artifact.getAttributes()) { for (BlackboardAttribute attribute : artifact.getAttributes()) {
final int attributeTypeID = attribute.getAttributeType().getTypeID(); final int attributeTypeID = attribute.getAttributeType().getTypeID();
//skip some internal attributes that user shouldn't see
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID() if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
@ -878,7 +902,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) { } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty(map, attribute); addEmailMsgProperty(map, attribute);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), sourceContent)); map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
} else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID() } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
&& attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) { && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
/* /*
@ -899,7 +923,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact attributes (artifact objID={0})", artifact.getId()), ex); //NON-NLS
} }
} }
@ -913,10 +937,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param attribute The attribute to use to make the map entry. * @param attribute The attribute to use to make the map entry.
*/ */
private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) { private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
final int attributeTypeID = attribute.getAttributeType().getTypeID(); final int attributeTypeID = attribute.getAttributeType().getTypeID();
// Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID() if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
@ -933,11 +954,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
map.put(attribute.getAttributeType().getDisplayName(), value); map.put(attribute.getAttributeType().getDisplayName(), value);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), sourceContent)); map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
} else { } else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
} }
} }
@Override @Override
@ -969,8 +989,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* source content. * source content.
* *
* @deprecated Do not use. The score property is now computed in a * @deprecated Do not use. The score property is now computed in a
* background thread and added to the property sheet in this node's event * background thread and added to the property sheet via property change
* PropertyChangeEventListner. * event.
*/ */
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S", @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
"BlackboardArtifactNode.createSheet.score.displayName=S", "BlackboardArtifactNode.createSheet.score.displayName=S",
@ -992,8 +1012,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param sheetSet The property sheet. * @param sheetSet The property sheet.
* *
* @deprecated Do not use. The tags property is now computed in a background * @deprecated Do not use. The tags property is now computed in a background
* thread and added to the property sheet in this node's event * thread and added to the property sheet via property change event.
* PropertyChangeEventListner.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"BlackboardArtifactNode.createSheet.tags.displayName=Tags"} "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
@ -1003,9 +1022,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
List<Tag> tags = new ArrayList<>(); List<Tag> tags = new ArrayList<>();
try { try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(sourceContent)); tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0}, content objID={1})", artifact.getId(), sourceContent.getId()), ex); logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
} }
sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", ")))); sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
} }
@ -1019,8 +1038,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* source content. * source content.
* *
* @deprecated Do not use. The tags property is now computed in a background * @deprecated Do not use. The tags property is now computed in a background
* thread and added to the property sheet in this node's event * thread and added to the property sheet via property change event.
* PropertyChangeEventListner.
*/ */
@Deprecated @Deprecated
protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) { protected final void addTagProperty(Sheet.Set sheetSet, List<Tag> tags) {
@ -1036,8 +1054,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* central repository lookup. * central repository lookup.
* *
* @deprecated Do not use. The count property is now computed in a * @deprecated Do not use. The count property is now computed in a
* background thread and added to the property sheet in this node's event * background thread and added to the property sheet via property change
* PropertyChangeEventListner. * event.
*/ */
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O", @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
"BlackboardArtifactNode.createSheet.count.displayName=O", "BlackboardArtifactNode.createSheet.count.displayName=O",
@ -1062,9 +1080,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param attribute The correlation attribute instance to use for the * @param attribute The correlation attribute instance to use for the
* central repository lookup. * central repository lookup.
* *
* @deprecated Do not use. The count property is now computed in a * @deprecated Do not use. The other occurrences property is now computed in a
* background thread and added to the property sheet in this node's event * background thread and added to the property sheet via property change
* PropertyChangeEventListner. * event.
*/ */
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C", @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
"BlackboardArtifactNode.createSheet.comment.displayName=C"}) "BlackboardArtifactNode.createSheet.comment.displayName=C"})