diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 8a3fbdae88870186d05c5f63d5b623d46dc953a2..945d1018e7c165d6e9313a19bebff50ab9bffe97 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -34,12 +34,16 @@ import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.datamodel.ArtifactStringContent; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.TskCoreException; /** * Instances of this class display the BlackboardArtifacts associated with the Content represented by a Node. @@ -471,7 +475,31 @@ protected ViewUpdate doInBackground() { index = artifacts.indexOf(artifact); if (index == -1) { index = 0; - } + } else { + // if the artifact has an ASSOCIATED ARTIFACT, then we display the associated artifact instead + try { + for (BlackboardAttribute attr : artifact.getAttributes()) { + if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { + long assocArtifactId = attr.getValueLong(); + int assocArtifactIndex = -1; + for (BlackboardArtifact art: artifacts) { + if (assocArtifactId == art.getArtifactID()) { + assocArtifactIndex = artifacts.indexOf(art); + break; + } + } + if (assocArtifactIndex >= 0) { + index = assocArtifactIndex; + } + break; + } + } + } + catch (TskCoreException ex) { + logger.log(Level.WARNING, "Couldn't get associated artifact to display in Content Viewer.", ex); + } + } + } if (isCancelled()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index eaccbc35e26eda410579022abfc50359dc896156..87494f2e1fa608bf007b830c51af839caac1db64 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -149,7 +149,12 @@ public AbstractNode visit(KeywordHits kh) { public AbstractNode visit(HashsetHits hh) { return hh.new HashsetHitsRootNode(); } - + + @Override + public AbstractNode visit(InterestingHits ih) { + return ih.new InterestingHitsRootNode(); + } + @Override public AbstractNode visit(EmailExtracted ee) { return ee.new EmailExtractedRootNode(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java index f57388ae65d92fac82355ec919b4d99eca4b00c6..27d505b489d17380ed51d9fb33e2917800483008 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java @@ -53,6 +53,8 @@ public interface AutopsyItemVisitor<T> { T visit(EmailExtracted ee); T visit(TagsNodeKey tagsNodeKey); + + T visit(InterestingHits ih); T visit(DataSources i); @@ -128,7 +130,11 @@ public T visit(KeywordHits kh) { public T visit(HashsetHits hh) { return defaultVisit(hh); } - + + @Override + public T visit(InterestingHits ih) { + return defaultVisit(ih); + } @Override public T visit(EmailExtracted ee) { return defaultVisit(ee); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 6166fdd4d644df698ee2a5cbaa819ac7ffd18d92..024546a808f41d38348a7096e01aa305047a2324 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -55,6 +55,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), }; /** @@ -232,7 +233,8 @@ private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifac final int attributeTypeID = attribute.getAttributeTypeID(); //skip some internal attributes that user shouldn't see 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()) { continue; } else { switch (attribute.getValueType()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index e2cef3846e2b6c09eff85584f66d0bbdaa817535..88921891fd68e1193718e0e6ccbfc930e6f11e5e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -27,6 +27,8 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; @@ -84,7 +86,11 @@ public interface DisplayableItemNodeVisitor<T> { T visit(EmailExtractedFolderNode eefn); T visit(TagsNode node); + + T visit(InterestingHitsRootNode ihrn); + T visit(InterestingHitsSetNode ihsn); + T visit(TagNameNode node); T visit(ContentTagTypeNode node); @@ -237,7 +243,17 @@ public T visit(HashsetHitsRootNode hhrn) { public T visit(HashsetHitsSetNode hhsn) { return defaultVisit(hhsn); } + + @Override + public T visit(InterestingHitsRootNode ihrn) { + return defaultVisit(ihrn); + } + @Override + public T visit(InterestingHitsSetNode ihsn) { + return defaultVisit(ihsn); + } + @Override public T visit(EmailExtractedRootNode eern) { return defaultVisit(eern); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java index 3559ad139559303b2344fcffd9bfe2671b23eb1d..41ae98a2d06ee0dab95ce6726e1ce93f02d42600 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContentChildren.java @@ -53,6 +53,8 @@ public ExtractedContentChildren(SleuthkitCase skCase) { doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT); doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java new file mode 100644 index 0000000000000000000000000000000000000000..cc165631f4ff3997f8516cf7476333a8aaf79be1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -0,0 +1,221 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.datamodel; + + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +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.Lookups; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskException; + + +public class InterestingHits implements AutopsyVisitableItem { + + private static final String INTERESTING_ITEMS = "INTERESTING ITEMS"; + private static final String DISPLAY_NAME = "Interesting Items"; + private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); + private SleuthkitCase skCase; + private Map<String, Set<Long>> interestingItemsMap; + + public InterestingHits(SleuthkitCase skCase) { + this.skCase = skCase; + interestingItemsMap = new LinkedHashMap<>(); + } + + @SuppressWarnings("deprecation") + private void initArtifacts() { + interestingItemsMap.clear(); + loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); + } + + /* + * Reads the artifacts of specified type, grouped by Set, and loads into the interestingItemsMap + */ + private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) { + ResultSet rs = null; + try { + int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); + int artId = artType.getTypeID(); + String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " + + "FROM blackboard_attributes,blackboard_artifacts WHERE " + + "attribute_type_id=" + setNameId + + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" + + " AND blackboard_artifacts.artifact_type_id=" + artId; + rs = skCase.runQuery(query); + while (rs.next()) { + String value = rs.getString("value_text"); + long artifactId = rs.getLong("artifact_id"); + if (!interestingItemsMap.containsKey(value)) { + interestingItemsMap.put(value, new HashSet<Long>()); + } + interestingItemsMap.get(value).add(artifactId); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "SQL Exception occurred: ", ex); + } + finally { + if (rs != null) { + try { + skCase.closeRunQuery(rs); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Error closing result set after getting artifacts", ex); + } + } + } + } + + @Override + public <T> T accept(AutopsyItemVisitor<T> v) { + return v.visit(this); + } + + /** + * Node for the interesting items + */ + public class InterestingHitsRootNode extends DisplayableItemNode { + + public InterestingHitsRootNode() { + super(Children.create(new InterestingHitsRootChildren(), true), Lookups.singleton(DISPLAY_NAME)); + super.setName(INTERESTING_ITEMS); + super.setDisplayName(DISPLAY_NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); + initArtifacts(); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public <T> T accept(DisplayableItemNodeVisitor<T> v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty("Name", + "Name", + "no description", + getName())); + + return s; + } + } + + private class InterestingHitsRootChildren extends ChildFactory<String> { + + @Override + protected boolean createKeys(List<String> list) { + list.addAll(interestingItemsMap.keySet()); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new InterestingHitsSetNode(key, interestingItemsMap.get(key)); + } + } + + public class InterestingHitsSetNode extends DisplayableItemNode { + + public InterestingHitsSetNode(String name, Set<Long> children) { + super(Children.create(new InterestingHitsSetChildren(children), true), Lookups.singleton(name)); + super.setName(name); + super.setDisplayName(name + " (" + children.size() + ")"); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty("Name", + "Name", + "no description", + getName())); + + return s; + } + + @Override + public <T> T accept(DisplayableItemNodeVisitor<T> v) { + return v.visit(this); + } + } + + private class InterestingHitsSetChildren extends ChildFactory<BlackboardArtifact> { + + private Set<Long> children; + + private InterestingHitsSetChildren(Set<Long> children) { + super(); + this.children = children; + } + + @Override + protected boolean createKeys(List<BlackboardArtifact> list) { + for (long l : children) { + try { + list.add(skCase.getBlackboardArtifact(l)); + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); + } + } + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact artifact) { + return new BlackboardArtifactNode(artifact); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java index d87f1f26701678ea40ff00d0ffde33e1ad5b8fb2..cbd1d941ea2d44f5833afb0e878e05d29a5f415a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ResultsNode.java @@ -35,6 +35,7 @@ public ResultsNode(SleuthkitCase sleuthkitCase) { new KeywordHits(sleuthkitCase), new HashsetHits(sleuthkitCase), new EmailExtracted(sleuthkitCase), + new InterestingHits(sleuthkitCase), new TagsNodeKey() )), Lookups.singleton(NAME)); setName(NAME); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 8b1e2ecc4734b82526ee381e3b677cd259de11cc..da6843c8d2034989c4aa946fde72581972d386af 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -84,7 +84,12 @@ public void refreshKeys(BlackboardArtifact.ARTIFACT_TYPE... types) { case TSK_TAG_ARTIFACT: if (o instanceof TagsNodeKey) this.refreshKey(o); - break; + break; + case TSK_INTERESTING_FILE_HIT: + case TSK_INTERESTING_ARTIFACT_HIT: + if (o instanceof InterestingHits) + this.refreshKey(o); + break; default: if (o instanceof ExtractedContent) this.refreshKey(o); @@ -96,9 +101,11 @@ public void refreshKeys(BlackboardArtifact.ARTIFACT_TYPE... types) { this.refreshKey(o); else if (o instanceof KeywordHits) this.refreshKey(o); + else if (o instanceof TagsNodeKey) + this.refreshKey(o); else if (o instanceof EmailExtracted) this.refreshKey(o); - else if (o instanceof TagsNodeKey) + else if (o instanceof InterestingHits) this.refreshKey(o); else if (o instanceof ExtractedContent) this.refreshKey(o); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 6dfd351ad6fa744da5fcd642403d1a48ee7399c0..b4e52a0f3c5e3e29f9c967b79fac75788bae281b 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -56,6 +56,8 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; import org.sleuthkit.autopsy.datamodel.ImageNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; @@ -184,7 +186,7 @@ public List<Action> visit(BlackboardArtifactNode ban) { final int artifactTypeID = ba.getArtifactTypeID(); if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() - || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() ) { actions.add(new ViewContextAction("View File in Directory", ban)); } else { // if the artifact links to another file, add an action to go to @@ -365,7 +367,17 @@ public AbstractAction visit(HashsetHitsRootNode hhrn) { public AbstractAction visit(HashsetHitsSetNode hhsn) { return openChild(hhsn); } + + @Override + public AbstractAction visit(InterestingHitsRootNode iarn) { + return openChild(iarn); + } + @Override + public AbstractAction visit(InterestingHitsSetNode iasn) { + return openChild(iasn); + } + @Override public AbstractAction visit(EmailExtractedRootNode eern) { return openChild(eern); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index f25e89e741250c3b70d0cc2e52327f74029d74d9..250bc12bcab4c42e1c6f0e57b5187e1a5e5e724e 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -969,6 +969,23 @@ public void viewArtifact(final BlackboardArtifact art) { } catch (TskException ex) { logger.log(Level.WARNING, "Error retrieving attributes", ex); } + } else if ( type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT) || + type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT) ) { + Node interestingItemsRootNode = resultsChilds.findChild(type.getLabel()); + Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); + try { + String setName = null; + List<BlackboardAttribute> attributes = art.getAttributes(); + for (BlackboardAttribute att : attributes) { + int typeId = att.getAttributeTypeID(); + if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { + setName = att.getValueString(); + } + } + treeNode = interestingItemsRootChildren.findChild(setName); + } catch (TskException ex) { + logger.log(Level.WARNING, "Error retrieving attributes", ex); + } } else { Node extractedContent = resultsChilds.findChild(ExtractedContentNode.NAME); Children extractedChilds = extractedContent.getChildren(); diff --git a/Core/src/org/sleuthkit/autopsy/images/interesting_item.png b/Core/src/org/sleuthkit/autopsy/images/interesting_item.png new file mode 100644 index 0000000000000000000000000000000000000000..f5388041327d6d38df094aff131c689bf9c85328 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/interesting_item.png differ diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 5978c64bef6cf3f3d9745b9a3ec753e6c7b8d51e..4942c41a145c373e924ee9348281fcbb5ff2094b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -302,7 +302,7 @@ private void cleanup() { commitTimer.stop(); searchTimer.stop(); commitTimer = null; - searchTimer = null; + //searchTimer = null; // do not collect, final searcher might still be running, in which case it throws an exception textExtractors.clear(); textExtractors = null;