mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-05 00:36:16 +00:00
First pass
This commit is contained in:
@@ -30,7 +30,7 @@ public class NodeProperty<T> extends PropertySupport.ReadOnly<T> {
|
||||
private T value;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public NodeProperty(String name, String displayName, String desc, T value) {
|
||||
public NodeProperty(String name, String displayName, String desc, T value) {
|
||||
super(name, (Class<T>) value.getClass(), displayName, desc);
|
||||
setValue("suppressCustomEditor", Boolean.TRUE); // remove the "..." (editing) button NON-NLS
|
||||
this.value = value;
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.AnalysisResultItem;
|
||||
import org.sleuthkit.autopsy.datamodel.FileTypeExtensions;
|
||||
@@ -30,7 +34,10 @@ import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultTableSearchResultsDT
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@@ -58,7 +65,7 @@ public class AnalysisResultNode extends ArtifactNode<AnalysisResult, AnalysisRes
|
||||
* @param iconPath The path for the node icon.
|
||||
*/
|
||||
AnalysisResultNode(AnalysisResultTableSearchResultsDTO tableData, AnalysisResultRowDTO resultRow, String iconPath) {
|
||||
super(resultRow, tableData.getColumns(), tableData.getArtifactType(), createLookup(resultRow), iconPath);
|
||||
super(tableData, resultRow, tableData.getColumns(), createLookup(resultRow), iconPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,4 +111,32 @@ public class AnalysisResultNode extends ArtifactNode<AnalysisResult, AnalysisRes
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<Tag>> getAllTagsFromDatabase() {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
try {
|
||||
List<BlackboardArtifactTag> artifactTags = ContentNodeUtil.getArtifactTagsFromDatabase(getRowDTO().getArtifact());
|
||||
if(!artifactTags.isEmpty()) {
|
||||
tags.addAll(artifactTags);
|
||||
}
|
||||
|
||||
List<ContentTag> contentTags = ContentNodeUtil.getContentTagsFromDatabase(getRowDTO().getSrcContent());
|
||||
if(!contentTags.isEmpty()) {
|
||||
tags.addAll(contentTags);
|
||||
}
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get content tags from database for Artifact id=" + getRowDTO().getArtifact().getId(), ex);
|
||||
}
|
||||
if(!tags.isEmpty()) {
|
||||
return Optional.of(tags);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,40 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
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.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LayoutFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LocalFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.datamodel.SlackFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.ArtifactRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.ColumnKey;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.SearchResultsDTO;
|
||||
import static org.sleuthkit.autopsy.mainui.nodes.BaseNode.backgroundTasksPool;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionContext;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.sco.SCOFetcher;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.sco.SCOSupporter;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@@ -48,23 +64,29 @@ import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
|
||||
public abstract class ArtifactNode<T extends BlackboardArtifact, R extends ArtifactRowDTO<T>> extends AbstractNode implements ActionContext {
|
||||
public abstract class ArtifactNode<T extends BlackboardArtifact, R extends ArtifactRowDTO<T>> extends BaseNode<SearchResultsDTO, ArtifactRowDTO> implements ActionContext, SCOSupporter {
|
||||
|
||||
private final R rowData;
|
||||
private final BlackboardArtifact.Type artifactType;
|
||||
private final List<ColumnKey> columns;
|
||||
private Node parentFileNode;
|
||||
|
||||
ArtifactNode(R rowData, List<ColumnKey> columns, BlackboardArtifact.Type artifactType, Lookup lookup, String iconPath) {
|
||||
super(Children.LEAF, lookup);
|
||||
ArtifactNode(SearchResultsDTO searchResults, R rowData, List<ColumnKey> columns, Lookup lookup, String iconPath) {
|
||||
super(Children.LEAF, lookup, searchResults, rowData);
|
||||
this.rowData = rowData;
|
||||
this.artifactType = artifactType;
|
||||
this.columns = columns;
|
||||
setupNodeDisplay(iconPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
backgroundTasksPool.submit(new SCOFetcher<>(new WeakReference<>(this)));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Content> getSourceContent() {
|
||||
return Optional.ofNullable(rowData.getSrcContent());
|
||||
@@ -133,7 +155,7 @@ public abstract class ArtifactNode<T extends BlackboardArtifact, R extends Artif
|
||||
public boolean supportsArtifactTagAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private Node getParentFileNode() {
|
||||
if (parentFileNode == null) {
|
||||
parentFileNode = getParentFileNode(rowData.getSrcContent());
|
||||
@@ -154,13 +176,80 @@ public abstract class ArtifactNode<T extends BlackboardArtifact, R extends Artif
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
return ContentNodeUtil.setSheet(super.createSheet(), columns, rowData.getCellValues());
|
||||
public Action[] getActions(boolean context) {
|
||||
return ActionsFactory.getActions(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
return ActionsFactory.getActions( this);
|
||||
public Optional<Content> getContent() {
|
||||
return Optional.of(rowData.getArtifact());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSheet(List<NodeProperty<?>> newProps) {
|
||||
super.updateSheet(newProps);
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"# {0} - occurrenceCount",
|
||||
"# {1} - attributeType",
|
||||
"ArtifactNode_createSheet_count_description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}",
|
||||
"ArtifactNode_createSheet_count_noCorrelationValues_description=Unable to find other occurrences because no value exists for the available correlation property"
|
||||
})
|
||||
@Override
|
||||
public Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription) {
|
||||
Long count = -1L;
|
||||
String description = defaultDescription;
|
||||
try {
|
||||
if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
|
||||
count = CentralRepository.getInstance().getCountCasesWithOtherInstances(attribute);
|
||||
description = Bundle.ArtifactNode_createSheet_count_description(count, attribute.getCorrelationType().getDisplayName());
|
||||
} else if (attribute != null) {
|
||||
description = Bundle.ArtifactNode_createSheet_count_noCorrelationValues_description();
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
getLogger().log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})",
|
||||
getRowDTO().getArtifact().getId(),
|
||||
attribute.getCorrelationType(),
|
||||
attribute.getCorrelationValue()), ex);
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
getLogger().log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})",
|
||||
getRowDTO().getArtifact().getId(),
|
||||
attribute.getCorrelationType(),
|
||||
attribute.getCorrelationValue()), ex);
|
||||
}
|
||||
return Pair.of(count, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
|
||||
/*
|
||||
* Has a tag with a comment been applied to the artifact or its source
|
||||
* content?
|
||||
*/
|
||||
DataResultViewerTable.HasCommentStatus status = tags.size() > 0 ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
for (Tag tag : tags) {
|
||||
if (!StringUtils.isBlank(tag.getComment())) {
|
||||
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Is there a comment in the CR for anything that matches the value and
|
||||
* type of the specified attributes.
|
||||
*/
|
||||
try {
|
||||
if (CentralRepoDbUtil.commentExistsOnAttributes(attributes)) {
|
||||
if (status == DataResultViewerTable.HasCommentStatus.TAG_COMMENT) {
|
||||
status = DataResultViewerTable.HasCommentStatus.CR_AND_TAG_COMMENTS;
|
||||
} else {
|
||||
status = DataResultViewerTable.HasCommentStatus.CR_COMMENT;
|
||||
}
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
getLogger().log(Level.SEVERE, "Attempted to Query CR for presence of comments in a Blackboard Artifact node and was unable to perform query, comment column will only reflect caseDB", ex);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,11 +18,18 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.BaseRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.SearchResultsDTO;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionContext;
|
||||
@@ -31,10 +38,24 @@ import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory;
|
||||
/**
|
||||
* A a simple starting point for nodes.
|
||||
*/
|
||||
abstract class BaseNode<S extends SearchResultsDTO, R extends BaseRowDTO> extends AbstractNode implements ActionContext {
|
||||
|
||||
public abstract class BaseNode<S extends SearchResultsDTO, R extends BaseRowDTO> extends AbstractNode implements ActionContext {
|
||||
|
||||
private final S results;
|
||||
private final R rowData;
|
||||
|
||||
/**
|
||||
* A pool of background tasks to run any long computation needed to populate
|
||||
* this node.
|
||||
*/
|
||||
static final ExecutorService backgroundTasksPool;
|
||||
private static final Integer MAX_POOL_SIZE = 10;
|
||||
|
||||
static {
|
||||
//Initialize this pool only once! This will be used by every instance BaseNode
|
||||
//to do their heavy duty SCO column and translation updates.
|
||||
backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
|
||||
new ThreadFactoryBuilder().setNameFormat("BaseNode-background-task-%d").build());
|
||||
}
|
||||
|
||||
BaseNode(Children children, Lookup lookup, S results, R rowData) {
|
||||
super(children, lookup);
|
||||
@@ -69,4 +90,41 @@ abstract class BaseNode<S extends SearchResultsDTO, R extends BaseRowDTO> extend
|
||||
public Action[] getActions(boolean context) {
|
||||
return ActionsFactory.getActions(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the values of the properties in the current property sheet with
|
||||
* the new properties being passed in. Only if that property exists in the
|
||||
* current sheet will it be applied. That way, we allow for subclasses to
|
||||
* add their own (or omit some!) properties and we will not accidentally
|
||||
* disrupt their UI.
|
||||
*
|
||||
* Race condition if not synchronized. Only one update should be applied at
|
||||
* a time.
|
||||
*
|
||||
* @param newProps New file property instances to be updated in the current
|
||||
* sheet.
|
||||
*/
|
||||
protected synchronized void updateSheet(List<NodeProperty<?>> newProps) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
/*
|
||||
* Refresh ONLY those properties in the sheet currently. Subclasses
|
||||
* may have only added a subset of our properties or their own
|
||||
* properties.
|
||||
*/
|
||||
Sheet visibleSheet = this.getSheet();
|
||||
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
|
||||
Property<?>[] visibleProps = visibleSheetSet.getProperties();
|
||||
for (NodeProperty<?> newProp : newProps) {
|
||||
for (int i = 0; i < visibleProps.length; i++) {
|
||||
if (visibleProps[i].getName().equals(newProp.getName())) {
|
||||
visibleProps[i] = newProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
visibleSheetSet.put(visibleProps);
|
||||
visibleSheet.put(visibleSheetSet);
|
||||
//setSheet() will notify Netbeans to update this node in the UI.
|
||||
this.setSheet(visibleSheet);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
AnalysisResultTypeFactory_adHocName=Adhoc Results
|
||||
# {0} - occurrenceCount
|
||||
# {1} - attributeType
|
||||
ArtifactNode_createSheet_count_description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}
|
||||
ArtifactNode_createSheet_count_noCorrelationValues_description=Unable to find other occurrences because no value exists for the available correlation property
|
||||
ImageNode_ExtractUnallocAction_text=Extract Unallocated Space to Single Files
|
||||
SearchResultRootNode_createSheet_childCount_displayName=Child Count
|
||||
SearchResultRootNode_createSheet_childCount_name=Child Count
|
||||
|
||||
@@ -18,17 +18,24 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.datamodel.TskContentItem;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.ColumnKey;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Utilities for setting up nodes that handle content.
|
||||
@@ -68,6 +75,12 @@ public class ContentNodeUtil {
|
||||
Object cellValue = values.get(i);
|
||||
|
||||
if (cellValue == null) {
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
columnKey.getFieldName(),
|
||||
columnKey.getDisplayName(),
|
||||
columnKey.getDescription(),
|
||||
""
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -85,4 +98,22 @@ public class ContentNodeUtil {
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tags from the case database that are associated with the file
|
||||
*
|
||||
* @return a list of tags that are associated with the file
|
||||
*/
|
||||
public static List<ContentTag> getContentTagsFromDatabase(Content content) throws TskCoreException, NoCurrentCaseException{
|
||||
List<ContentTag> tags = new ArrayList<>();
|
||||
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static List<BlackboardArtifactTag> getArtifactTagsFromDatabase(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException{
|
||||
List<BlackboardArtifactTag> tags = new ArrayList<>();
|
||||
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,23 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.DataArtifactItem;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactTableSearchResultsDTO;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* node to display a data artifact.
|
||||
@@ -48,6 +57,27 @@ public class DataArtifactNode extends ArtifactNode<DataArtifact, DataArtifactRow
|
||||
}
|
||||
|
||||
public DataArtifactNode(DataArtifactTableSearchResultsDTO tableData, DataArtifactRowDTO artifactRow, String iconPath) {
|
||||
super(artifactRow, tableData.getColumns(), tableData.getArtifactType(), createLookup(artifactRow), iconPath);
|
||||
super(tableData, artifactRow, tableData.getColumns(), createLookup(artifactRow), iconPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<Tag>> getAllTagsFromDatabase() {
|
||||
try {
|
||||
List<BlackboardArtifactTag> artifactTags = ContentNodeUtil.getArtifactTagsFromDatabase(getRowDTO().getArtifact());
|
||||
if(!artifactTags.isEmpty()) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
tags.addAll(artifactTags);
|
||||
return Optional.of(tags);
|
||||
}
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get content tags from database for Artifact id=" + getRowDTO().getArtifact().getId(), ex);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,25 +18,34 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.datamodel.FileTypeExtensions;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.SearchResultsDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.ColumnKey;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.ExtensionMediaType;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.LayoutFileRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.SlackFileRowDTO;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionContext;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.sco.SCOFetcher;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.sco.SCOSupporter;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
|
||||
@@ -45,7 +54,9 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
||||
/**
|
||||
* A node for representing an AbstractFile.
|
||||
*/
|
||||
public class FileNode extends AbstractNode implements ActionContext {
|
||||
public class FileNode extends BaseNode<SearchResultsDTO, FileRowDTO> implements SCOSupporter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FileNode.class.getName());
|
||||
|
||||
/**
|
||||
* Gets the path to the icon file that should be used to visually represent
|
||||
@@ -95,7 +106,7 @@ public class FileNode extends AbstractNode implements ActionContext {
|
||||
|
||||
public FileNode(SearchResultsDTO results, FileRowDTO file, boolean directoryBrowseMode) {
|
||||
// GVDTODO: at some point, this leaf will need to allow for children
|
||||
super(Children.LEAF, ContentNodeUtil.getLookup(file.getAbstractFile()));
|
||||
super(Children.LEAF, ContentNodeUtil.getLookup(file.getAbstractFile()), results, file);
|
||||
setIcon(file);
|
||||
setDisplayName(ContentNodeUtil.getContentDisplayName(file.getFileName()));
|
||||
setName(ContentNodeUtil.getContentName(file.getId()));
|
||||
@@ -187,7 +198,40 @@ public class FileNode extends AbstractNode implements ActionContext {
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
return ContentNodeUtil.setSheet(super.createSheet(), this.columns, this.fileData.getCellValues());
|
||||
Sheet sheet = super.createSheet();
|
||||
backgroundTasksPool.submit(new SCOFetcher<>(new WeakReference<>(this)));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Content> getContent() {
|
||||
return Optional.ofNullable(fileData.getAbstractFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSheet(List<NodeProperty<?>> newProps) {
|
||||
super.updateSheet(newProps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<Tag>> getAllTagsFromDatabase() {
|
||||
try {
|
||||
List<ContentTag> contentTags = ContentNodeUtil.getContentTagsFromDatabase(fileData.getAbstractFile());
|
||||
if(!contentTags.isEmpty()) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
tags.addAll(contentTags);
|
||||
return Optional.of(tags);
|
||||
}
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get content tags from database for AbstractFile id=" + fileData.getAbstractFile().getId(), ex);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
SCOFetcher_comment_display_name=C
|
||||
SCOFetcher_count_display_name=O
|
||||
SCOFetcher_occurrences_defaultDescription=No correlation properties found
|
||||
SCOFetcher_occurrences_multipleProperties=Multiple different correlation properties exist for this result
|
||||
SCOFetcher_score_display_name=S
|
||||
SCOSupporter.valueLoading=value loading
|
||||
# {0} - significanceDisplayName
|
||||
SCOSupporter_getScorePropertyAndDescription_description=Has an {0} analysis result score
|
||||
SCOSupporter_nodescription_text=no description
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes.sco;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.mainui.nodes.sco.SCOFetcher.SCOData;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountInstance;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kelly
|
||||
*/
|
||||
public class SCOFetcher<T extends Content> extends SwingWorker<SCOData, Void> {
|
||||
|
||||
private final WeakReference<SCOSupporter> weakSupporterRef;
|
||||
private static final Logger logger = Logger.getLogger(SCOFetcher.class.getName());
|
||||
|
||||
public SCOFetcher(WeakReference<SCOSupporter> weakSupporterRef) {
|
||||
this.weakSupporterRef = weakSupporterRef;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"SCOFetcher_occurrences_defaultDescription=No correlation properties found",
|
||||
"SCOFetcher_occurrences_multipleProperties=Multiple different correlation properties exist for this result"})
|
||||
@Override
|
||||
protected SCOData doInBackground() throws Exception {
|
||||
SCOSupporter scoSupporter = weakSupporterRef.get();
|
||||
Content content = scoSupporter.getContent().get();
|
||||
//Check for stale reference or if columns are disabled
|
||||
if (content == null || UserPreferences.getHideSCOColumns()) {
|
||||
return null;
|
||||
}
|
||||
// get the SCO column values
|
||||
Pair<Score, String> scoreAndDescription;
|
||||
Pair<Long, String> countAndDescription = null;
|
||||
scoreAndDescription = scoSupporter.getScorePropertyAndDescription();
|
||||
|
||||
String description = Bundle.SCOFetcher_occurrences_defaultDescription();
|
||||
List<CorrelationAttributeInstance> listOfPossibleAttributes = new ArrayList<>();
|
||||
//the lists returned will be empty if the CR is not enabled
|
||||
if (content instanceof AbstractFile) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AbstractFile) content));
|
||||
} else if (content instanceof AnalysisResult) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) content));
|
||||
} else if (content instanceof DataArtifact) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) content));
|
||||
} else if (content instanceof OsAccount) {
|
||||
try {
|
||||
List<OsAccountInstance> osAccountInstances = ((OsAccount) content).getOsAccountInstances();
|
||||
|
||||
/*
|
||||
* In the most common use cases it will not matter which
|
||||
* OsAccountInstance is selected, so choosing the first one is
|
||||
* the most efficient solution.
|
||||
*/
|
||||
OsAccountInstance osAccountInstance = osAccountInstances.isEmpty() ? null : osAccountInstances.get(0);
|
||||
/*
|
||||
* If we have a Case whith both data sources in the CR and data
|
||||
* sources not in the CR, some of the OsAccountInstances for
|
||||
* this OsAccount have not been processed into the CR. In this
|
||||
* situation the counts may not always be accurate or
|
||||
* consistent.
|
||||
*
|
||||
* In order to ensure conistency in all use cases we would need
|
||||
* to ensure we always had an OsAccountInstance whose data
|
||||
* source was in the CR when such an OsAccountInstance was
|
||||
* available.
|
||||
*
|
||||
* The following block of code has been commented out because it
|
||||
* reduces efficiency in what are believed to be the most common
|
||||
* use cases. It would serve the purpose of providing
|
||||
* consistency in edge cases where users are putting some but
|
||||
* not all the data concerning OS Accounts, which is present in
|
||||
* a single Case, into the CR. See TODO-JIRA-8031 for a similar
|
||||
* issue in the OO viewer.
|
||||
*/
|
||||
|
||||
// if (CentralRepository.isEnabled() && !osAccountInstances.isEmpty()) {
|
||||
// try {
|
||||
// CentralRepository centralRepo = CentralRepository.getInstance();
|
||||
// //Correlation Cases are cached when we get them so this shouldn't involve a round trip for every node.
|
||||
// CorrelationCase crCase = centralRepo.getCase(Case.getCurrentCaseThrows());
|
||||
// for (OsAccountInstance caseOsAccountInstance : osAccountInstances) {
|
||||
// //correlation data sources are also cached so once should not involve round trips every time.
|
||||
// CorrelationDataSource correlationDataSource = centralRepo.getDataSource(crCase, caseOsAccountInstance.getDataSource().getId());
|
||||
// if (correlationDataSource != null) {
|
||||
// //we have found a data source which exists in the CR we will use it instead of the arbitrary first instance
|
||||
// osAccountInstance = caseOsAccountInstance;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } catch (CentralRepoException ex) {
|
||||
// logger.log(Level.SEVERE, "Error checking CR for data sources which exist in it", ex);
|
||||
// } catch (NoCurrentCaseException ex) {
|
||||
// logger.log(Level.WARNING, "The current case was closed while attempting to find a data source in the central repository", ex);
|
||||
// }
|
||||
// }
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(osAccountInstance));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to get the DataSource or OsAccountInstances from an OsAccount with ID: " + content.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
Optional<List<Tag>> optionalList = scoSupporter.getAllTagsFromDatabase();
|
||||
|
||||
DataResultViewerTable.HasCommentStatus commentStatus = DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
|
||||
if(optionalList.isPresent()) {
|
||||
commentStatus = scoSupporter.getCommentProperty(optionalList.get(), listOfPossibleAttributes);
|
||||
}
|
||||
|
||||
CorrelationAttributeInstance corInstance = null;
|
||||
if (CentralRepository.isEnabled()) {
|
||||
if (listOfPossibleAttributes.size() > 1) {
|
||||
//Don't display anything if there is more than 1 correlation property for an artifact but let the user know
|
||||
description = Bundle.SCOFetcher_occurrences_multipleProperties();
|
||||
} else if (!listOfPossibleAttributes.isEmpty()) {
|
||||
//there should only be one item in the list
|
||||
corInstance = listOfPossibleAttributes.get(0);
|
||||
}
|
||||
countAndDescription = scoSupporter.getCountPropertyAndDescription(corInstance, description);
|
||||
}
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SCOData(scoreAndDescription, commentStatus, countAndDescription);
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"SCOFetcher_score_display_name=S",
|
||||
"SCOFetcher_comment_display_name=C",
|
||||
"SCOFetcher_count_display_name=O"
|
||||
|
||||
})
|
||||
@Override
|
||||
public void done() {
|
||||
if (isCancelled() || UserPreferences.getHideSCOColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
SCOData data = get();
|
||||
|
||||
if(data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<NodeProperty<?>> props = new ArrayList<>();
|
||||
|
||||
if(data.getScoreAndDescription() != null) {
|
||||
props.add(new NodeProperty<>(
|
||||
Bundle.SCOFetcher_score_display_name(),
|
||||
Bundle.SCOFetcher_score_display_name(),
|
||||
data.getScoreAndDescription().getRight(),
|
||||
data.getScoreAndDescription().getLeft()));
|
||||
}
|
||||
|
||||
if(data.getComment() != null) {
|
||||
props.add(new NodeProperty<>(
|
||||
Bundle.SCOFetcher_comment_display_name(),
|
||||
Bundle.SCOFetcher_comment_display_name(),
|
||||
"",
|
||||
data.getComment()));
|
||||
}
|
||||
|
||||
if(data.getCountAndDescription() != null) {
|
||||
props.add(new NodeProperty<>(
|
||||
Bundle.SCOFetcher_count_display_name(),
|
||||
Bundle.SCOFetcher_count_display_name(),
|
||||
data.getCountAndDescription().getRight(),
|
||||
data.getCountAndDescription().getLeft()));
|
||||
}
|
||||
|
||||
SCOSupporter scoSupporter = weakSupporterRef.get();
|
||||
|
||||
if(!props.isEmpty() && scoSupporter != null) {
|
||||
scoSupporter.updateSheet(props);
|
||||
}
|
||||
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SCOData {
|
||||
|
||||
private final Pair<Score, String> scoreAndDescription;
|
||||
private final DataResultViewerTable.HasCommentStatus comment;
|
||||
private final Pair<Long, String> countAndDescription;
|
||||
|
||||
SCOData(Pair<Score, String> scoreAndDescription, DataResultViewerTable.HasCommentStatus comment, Pair<Long, String> countAndDescription) {
|
||||
this.scoreAndDescription = scoreAndDescription;
|
||||
this.comment = comment;
|
||||
this.countAndDescription = countAndDescription;
|
||||
}
|
||||
|
||||
Pair<Score, String> getScoreAndDescription() {
|
||||
return scoreAndDescription;
|
||||
}
|
||||
|
||||
DataResultViewerTable.HasCommentStatus getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
Pair<Long, String> getCountAndDescription() {
|
||||
return countAndDescription;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.mainui.nodes.sco;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface SCOSupporter {
|
||||
|
||||
@NbBundle.Messages({"SCOSupporter_nodescription_text=no description",
|
||||
"SCOSupporter.valueLoading=value loading"})
|
||||
static final String NO_DESCR = Bundle.SCOSupporter_nodescription_text();
|
||||
|
||||
default Optional<Content> getContent() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
default Optional<List<Tag>> getAllTagsFromDatabase() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
default void updateSheet(List<NodeProperty<?>> newProps) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Logger getLogger();
|
||||
|
||||
/**
|
||||
* Returns Score property for the content.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - significanceDisplayName",
|
||||
"SCOSupporter_getScorePropertyAndDescription_description=Has an {0} analysis result score"
|
||||
})
|
||||
default Pair<Score, String> getScorePropertyAndDescription() {
|
||||
Score score = Score.SCORE_UNKNOWN;
|
||||
Optional<Content> optional = getContent();
|
||||
if (optional.isPresent()) {
|
||||
Content content = optional.get();
|
||||
try {
|
||||
score = content.getAggregateScore();
|
||||
} catch (TskCoreException ex) {
|
||||
getLogger().log(Level.WARNING, "Unable to get aggregate score for content with id: " + content.getId(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
String significanceDisplay = score.getSignificance().getDisplayName();
|
||||
String description = Bundle.SCOSupporter_getScorePropertyAndDescription_description(significanceDisplay);
|
||||
return Pair.of(score, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns comment property for the node.
|
||||
*
|
||||
* Default implementation is a null implementation.
|
||||
*
|
||||
* @param tags The list of tags.
|
||||
* @param attributes The list of correlation attribute instances.
|
||||
*
|
||||
* @return Comment property for the underlying content of the node.
|
||||
*/
|
||||
default DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
|
||||
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns occurrences/count property for the node.
|
||||
*
|
||||
* Default implementation is a null implementation.
|
||||
*
|
||||
* @param attribute The correlation attribute for which data will
|
||||
* be retrieved.
|
||||
* @param defaultDescription A description to use when none is determined by
|
||||
* the getCountPropertyAndDescription method.
|
||||
*
|
||||
* @return count property for the underlying content of the node.
|
||||
*/
|
||||
default Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute, String defaultDescription) {
|
||||
return Pair.of(-1L, NO_DESCR);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user