diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java index 26d1c7a9bc..06a3e2e39e 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddBlackboardArtifactTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2019 Basis Technology Corp. + * Copyright 2013-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TagName; @@ -46,6 +47,8 @@ import org.sleuthkit.datamodel.TskCoreException; }) public class AddBlackboardArtifactTagAction extends AddTagAction { + private static final long serialVersionUID = 1L; + // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). @@ -82,8 +85,14 @@ public class AddBlackboardArtifactTagAction extends AddTagAction { * invocation of addTag(), we don't want to tag the same * BlackboardArtifact more than once, so we dedupe the * BlackboardArtifacts by stuffing them into a HashSet. + * + * RC (9/8/21): The documentation does NOT say that lookupAll() can + * return duplicates. That would be very broken. What motivated this + * "de-duping" ? */ - selectedArtifacts.addAll(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)); + for (BlackboardArtifactItem item : Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class)) { + selectedArtifacts.add(item.getTskContent()); + } } else { for (Content content : getContentToTag()) { if (content instanceof BlackboardArtifact) { @@ -111,4 +120,10 @@ public class AddBlackboardArtifactTagAction extends AddTagAction { } }).start(); } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index 9c059205f5..ff3dc08e04 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2020 Basis Technology Corp. + * Copyright 2013-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,22 +44,29 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** - * An abstract base class for Actions that allow users to tag SleuthKit data + * An abstract super class for Actions that allow users to tag Sleuth Kit data * model objects. */ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { private static final long serialVersionUID = 1L; private static final String NO_COMMENT = ""; - private final Collection content = new HashSet<>(); + private final Collection contentObjsToTag; + /** + * Constructs an instance of an abstract super class for Actions that allow + * users to tag Sleuth Kit data model objects. + * + * @param menuText The menu item text. + */ AddTagAction(String menuText) { super(menuText); + contentObjsToTag = new HashSet<>(); } @Override public JMenuItem getPopupPresenter() { - content.clear(); + contentObjsToTag.clear(); return new TagMenu(); } @@ -70,7 +77,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { * @return The specified content for this action. */ Collection getContentToTag() { - return Collections.unmodifiableCollection(content); + return Collections.unmodifiableCollection(contentObjsToTag); } /** @@ -83,8 +90,8 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { * apply to the Content specified. */ public JMenuItem getMenuForContent(Collection contentToTag) { - content.clear(); - content.addAll(contentToTag); + contentObjsToTag.clear(); + contentObjsToTag.addAll(contentToTag); return new TagMenu(); } @@ -111,6 +118,11 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { */ abstract protected void addTag(TagName tagName, String comment); + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + /** * Instances of this class implement a context menu user interface for * creating or selecting a tag name for a tag and specifying an optional tag @@ -126,7 +138,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { super(getActionDisplayName()); // Get the current set of tag names. - Map tagNamesMap = null; + Map tagNamesMap; List standardTagNames = TagsManager.getStandardTagNames(); Map tagSetMenuMap = new HashMap<>(); List standardTagMenuitems = new ArrayList<>(); @@ -240,5 +252,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { return tagNameItem; } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java index 5f47487f94..b1069ba5b9 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.centralrepository; import java.awt.event.ActionEvent; +import java.util.List; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.Action; @@ -64,7 +65,13 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction { correlationAttributeInstance = CorrelationAttributeUtil.getCorrAttrForFile(file); if (correlationAttributeInstance == null) { addToDatabase = true; - correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttrFromFile(file); + final List md5CorrelationAttr = CorrelationAttributeUtil.makeCorrAttrsForSearch(file); + if (!md5CorrelationAttr.isEmpty()) { + //for an abstract file the 'list' of attributes will be a single attribute or empty and is returning a list for consistancy with other makeCorrAttrsForSearch methods per 7852 + correlationAttributeInstance = md5CorrelationAttr.get(0); + } else { + correlationAttributeInstance = null; + } } if (file.getSize() == 0) { putValue(Action.NAME, Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoCommentEmptyFile()); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java b/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java index 80e4ada388..643f5d5de7 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/application/OtherOccurrences.java @@ -53,7 +53,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; -import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccountInstance; import org.sleuthkit.datamodel.SleuthkitCase; @@ -116,7 +115,7 @@ public final class OtherOccurrences { // correlate on blackboard artifact attributes if they exist and supported BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); if (bbArtifact != null && CentralRepository.isEnabled()) { - ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact)); + ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(bbArtifact)); } // we can correlate based on the MD5 if it is enabled diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED index a80f1f7d86..724758847b 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Bundle.properties-MERGED @@ -18,18 +18,18 @@ CentralRepositoryService.serviceName=Central Repository Service CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'. CorrelationAttributeInstance.nullName.message=Database name is null. CorrelationAttributeUtil.emailaddresses.text=Email Addresses -CorrelationType.DOMAIN.displayName=Domains -CorrelationType.EMAIL.displayName=Email Addresses -CorrelationType.FILES.displayName=Files +CorrelationType.DOMAIN.displayName=Domain +CorrelationType.EMAIL.displayName=Email Address +CorrelationType.FILES.displayName=File MD5 CorrelationType.ICCID.displayName=ICCID Number CorrelationType.IMEI.displayName=IMEI Number CorrelationType.IMSI.displayName=IMSI Number -CorrelationType.MAC.displayName=MAC Addresses +CorrelationType.MAC.displayName=MAC Address CorrelationType.OS_ACCOUNT.displayName=Os Account -CorrelationType.PHONE.displayName=Phone Numbers -CorrelationType.PROG_NAME.displayName=Installed Programs -CorrelationType.SSID.displayName=Wireless Networks -CorrelationType.USBID.displayName=USB Devices +CorrelationType.PHONE.displayName=Phone Number +CorrelationType.PROG_NAME.displayName=Installed Program +CorrelationType.SSID.displayName=Wireless Network +CorrelationType.USBID.displayName=USB Device EamArtifactInstances.knownStatus.bad=Bad EamArtifactInstances.knownStatus.known=Known EamArtifactInstances.knownStatus.unknown=Unknown diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java index f7d423c85d..300f1019c2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java @@ -282,7 +282,7 @@ final public class CorrelationAttributeNormalizer { if (imsiWithoutSeperators.matches(validImsiRegex)) { return imsiWithoutSeperators; } else { - throw new CorrelationAttributeNormalizationException("Data provided was not a valid Imsi. : " + data); + throw new CorrelationAttributeNormalizationException("Data provided was not a valid IMSI. : " + data); } } @@ -306,7 +306,7 @@ final public class CorrelationAttributeNormalizer { if (macWithoutSeperators.matches(validMacRegex)) { return macWithoutSeperators; } else { - throw new CorrelationAttributeNormalizationException("Data provided was not a valid Imsi. : " + data); + throw new CorrelationAttributeNormalizationException("Data provided was not a valid MAC address. : " + data); } } @@ -335,7 +335,7 @@ final public class CorrelationAttributeNormalizer { if (imeiWithoutSeperators.matches(validImeiRegex)) { return imeiWithoutSeperators; } else { - throw new CorrelationAttributeNormalizationException("Data provided was not a valid Imsi. : " + data); + throw new CorrelationAttributeNormalizationException("Data provided was not a valid IMEI. : " + data); } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java index 6d2ff51c01..5e64e3994b 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2017-2020 Basis Technology Corp. + * Copyright 2017-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.Cent import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -41,7 +42,6 @@ import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.InvalidAccountIDException; import org.sleuthkit.datamodel.OsAccount; -import org.sleuthkit.datamodel.OsAccountInstance; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -55,7 +55,7 @@ public class CorrelationAttributeUtil { private static final List domainsToSkip = Arrays.asList("localhost", "127.0.0.1"); // artifact ids that specifically have a TSK_DOMAIN attribute that should be handled by CR - private static Set DOMAIN_ARTIFACT_TYPE_IDS = new HashSet<>(Arrays.asList( + private static final Set DOMAIN_ARTIFACT_TYPE_IDS = new HashSet<>(Arrays.asList( ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID(), ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), @@ -69,7 +69,7 @@ public class CorrelationAttributeUtil { * for the email address correlation attribute type. This string is * duplicated in the CorrelationAttributeInstance class. * - * TODO (Jira-6088): We should not have multiple deifnitions of this string. + * TODO (Jira-6088): We should not have multiple definitions of this string. * * @return The display name of the email address correlation attribute type. */ @@ -78,56 +78,45 @@ public class CorrelationAttributeUtil { return Bundle.CorrelationAttributeUtil_emailaddresses_text(); } - // Defines which artifact types act as the sources for CR data. - // Most notably, does not include KEYWORD HIT, CALLLOGS, MESSAGES, CONTACTS - // TSK_INTERESTING_ARTIFACT_HIT (See JIRA-6129 for more details on the - // interesting artifact hit). - // IMPORTANT: This set should be updated for new artifacts types that need to - // be inserted into the CR. - private static final Set SOURCE_TYPES_FOR_CR_INSERT = new HashSet() { - { - addAll(DOMAIN_ARTIFACT_TYPE_IDS); - - add(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()); - add(ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()); - add(ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()); - add(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()); - add(ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()); - add(ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()); - add(ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()); - add(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()); - add(ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()); - add(ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID()); + private static List makeCorrAttrsToSave(DataArtifact artifact) { + int artifactTypeID = artifact.getArtifactTypeID(); + //The account fields in these types are expected to be saved in a TSK_ACCOUNT artifact, which will be processed + if (artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) { + return new ArrayList<>(); } - }; - - /** - * Makes zero to many correlation attribute instances from the attributes of - * artifacts that have correlatable data. The intention of this method is to - * use the results to save to the CR, not to correlate with them. If you - * want to correlate, please use makeCorrAttrsForCorrelation. An artifact - * that can have correlatable data != An artifact that should be the source - * of data in the CR, so results may be un-necessarily incomplete. - * - * @param artifact An artifact. - * - * @return A list, possibly empty, of correlation attribute instances for - * the artifact. - */ - public static List makeCorrAttrsToSave(BlackboardArtifact artifact) { - if (SOURCE_TYPES_FOR_CR_INSERT.contains(artifact.getArtifactTypeID())) { - // Restrict the correlation attributes to use for saving. - // The artifacts which are suitable for saving are a subset of the - // artifacts that are suitable for correlating. - return makeCorrAttrsForCorrelation(artifact); - } - // Return an empty collection. - return new ArrayList<>(); + return CorrelationAttributeUtil.makeCorrAttrsForSearch(artifact); } /** * Makes zero to many correlation attribute instances from the attributes of - * artifacts that have correlatable data. The intention of this method is to + * content objects that have correlatable data. The intention of this method + * is to use the results to save to the CR, not to correlate with them. If + * you want to correlate, please use makeCorrAttrsForSearch. An artifact + * that can have correlatable data != An artifact that should be the source + * of data in the CR, so results may be un-necessarily incomplete. + * + * @param content A Content object. + * + * @return A list, possibly empty, of correlation attribute instances for + * the content. + */ + public static List makeCorrAttrsToSave(Content content) { + if (content instanceof DataArtifact) { + return makeCorrAttrsToSave((DataArtifact) content); + } else if (content instanceof AnalysisResult) { + //AnalysisResults should already have the correlation attributes they are correlating on saved + //This check replaces the check explicitly excluding keyword hits and interesting items that existed prior to the AnalysisResult designation + return new ArrayList<>(); + } else { + return makeCorrAttrsForSearch(content); + } + } + + /** + * Makes zero to many correlation attribute instances from the attributes of + * content that have correlatable data. The intention of this method is to * use the results to correlate with, not to save. If you want to save, * please use makeCorrAttrsToSave. An artifact that can have correlatable * data != An artifact that should be the source of data in the CR, so @@ -144,96 +133,126 @@ public class CorrelationAttributeUtil { * whether receiving a null return value is an error or not, plus null * checking is easy to forget, while catching exceptions is enforced. * - * @param artifact An artifact. + * @param Content A Content object. * * @return A list, possibly empty, of correlation attribute instances for - * the artifact. + * the content. */ - public static List makeCorrAttrsForCorrelation(BlackboardArtifact artifact) { + public static List makeCorrAttrsForSearch(Content content) { + if (content instanceof DataArtifact) { + return makeCorrAttrsForSearch((DataArtifact) content); + } else if (content instanceof AnalysisResult) { + return makeCorrAttrsForSearch((AnalysisResult) content); + } else if (content instanceof AbstractFile) { + return makeCorrAttrsForSearch((AbstractFile) content); + } else { + return new ArrayList<>(); + } + } + + private static List makeCorrAttrsForSearch(AnalysisResult analysisResult) { List correlationAttrs = new ArrayList<>(); try { - BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact); - if (sourceArtifact != null) { - - List attributes = sourceArtifact.getAttributes(); - - int artifactTypeID = sourceArtifact.getArtifactTypeID(); - if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { - BlackboardAttribute setNameAttr = getAttribute(attributes, new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); - if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID, attributes); - } - } else if (DOMAIN_ARTIFACT_TYPE_IDS.contains(artifactTypeID)) { - BlackboardAttribute domainAttr = getAttribute(attributes, new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN)); - if ((domainAttr != null) - && !domainsToSkip.contains(domainAttr.getValueString())) { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID, attributes); - } - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) { - // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times - Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(sourceArtifact.getObjectID()); - Content dataSource = sourceContent.getDataSource(); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID, - attributes, sourceContent, dataSource); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID, - attributes, sourceContent, dataSource); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID, CorrelationAttributeInstance.SSID_TYPE_ID, attributes); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()) { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID, attributes); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()) { - // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times - Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(sourceArtifact.getObjectID()); - Content dataSource = sourceContent.getDataSource(); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI, CorrelationAttributeInstance.IMEI_TYPE_ID, - attributes, sourceContent, dataSource); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID, - attributes, sourceContent, dataSource); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID, - attributes, sourceContent, dataSource); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()) { - // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times - Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(sourceArtifact.getObjectID()); - Content dataSource = sourceContent.getDataSource(); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID, - attributes, sourceContent, dataSource); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID, - attributes, sourceContent, dataSource); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) { - // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times - Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(sourceArtifact.getObjectID()); - Content dataSource = sourceContent.getDataSource(); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID, - attributes, sourceContent, dataSource); - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID, - attributes, sourceContent, dataSource); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { - makeCorrAttrFromAcctArtifact(correlationAttrs, sourceArtifact); - - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID()) { - BlackboardAttribute setNameAttr = getAttribute(attributes, new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH)); - String pathAttrString = null; - if (setNameAttr != null) { - pathAttrString = setNameAttr.getValueString(); - } - if (pathAttrString != null && !pathAttrString.isEmpty()) { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID, attributes); - } else { - makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID, attributes); - } - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) { - makeCorrAttrsFromCommunicationArtifacts(correlationAttrs, sourceArtifact, attributes); + int artifactTypeID = analysisResult.getArtifactTypeID(); + if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + BlackboardAttribute setNameAttr = analysisResult.getAttribute(BlackboardAttribute.Type.TSK_SET_NAME); + if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) { + makeCorrAttrFromArtifactAttr(correlationAttrs, analysisResult, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID, analysisResult.getAttributes()); } + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { + BlackboardAttribute assocArtifactAttr = analysisResult.getAttribute(BlackboardAttribute.Type.TSK_ASSOCIATED_ARTIFACT); + if (assocArtifactAttr != null) { + BlackboardArtifact sourceArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(assocArtifactAttr.getValueLong()); + return CorrelationAttributeUtil.makeCorrAttrsForSearch(sourceArtifact); + } + } + correlationAttrs.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(analysisResult.getParent())); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get information regarding correlation attributes from AnalysisResult", ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Attempted to retrieve correlation attributes for search with no currently open case.", ex); + } catch (CentralRepoException ex) { + logger.log(Level.SEVERE, "Failed to get correlation type from central repository.", ex); + } + return correlationAttrs; + } + + private static List makeCorrAttrsForSearch(DataArtifact artifact) { + List correlationAttrs = new ArrayList<>(); + try { + + List attributes = artifact.getAttributes(); + + int artifactTypeID = artifact.getArtifactTypeID(); + if (DOMAIN_ARTIFACT_TYPE_IDS.contains(artifactTypeID)) { + BlackboardAttribute domainAttr = getAttribute(attributes, new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN)); + if ((domainAttr != null) + && !domainsToSkip.contains(domainAttr.getValueString())) { + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID, attributes); + } + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) { + // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times + Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); + Content dataSource = sourceContent.getDataSource(); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID, + attributes, sourceContent, dataSource); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID, + attributes, sourceContent, dataSource); + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) { + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID, CorrelationAttributeInstance.SSID_TYPE_ID, attributes); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()) { + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID, attributes); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()) { + // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times + Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); + Content dataSource = sourceContent.getDataSource(); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI, CorrelationAttributeInstance.IMEI_TYPE_ID, + attributes, sourceContent, dataSource); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID, + attributes, sourceContent, dataSource); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID, + attributes, sourceContent, dataSource); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()) { + // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times + Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); + Content dataSource = sourceContent.getDataSource(); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID, + attributes, sourceContent, dataSource); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID, + attributes, sourceContent, dataSource); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) { + // prefetch all the information as we will be calling makeCorrAttrFromArtifactAttr() multiple times + Content sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); + Content dataSource = sourceContent.getDataSource(); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID, + attributes, sourceContent, dataSource); + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID, + attributes, sourceContent, dataSource); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { + makeCorrAttrFromAcctArtifact(correlationAttrs, artifact); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID()) { + BlackboardAttribute setNameAttr = getAttribute(attributes, new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH)); + String pathAttrString = null; + if (setNameAttr != null) { + pathAttrString = setNameAttr.getValueString(); + } + if (pathAttrString != null && !pathAttrString.isEmpty()) { + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID, attributes); + } else { + makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID, attributes); + } + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) { + makeCorrAttrsFromCommunicationArtifacts(correlationAttrs, artifact, attributes); } } catch (CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, String.format("Error normalizing correlation attribute (%s)", artifact), ex); // NON-NLS @@ -292,8 +311,6 @@ public class CorrelationAttributeUtil { */ private static void makeCorrAttrsFromCommunicationArtifacts(List corrAttrInstances, BlackboardArtifact artifact, List attributes) throws TskCoreException, CentralRepoException, CorrelationAttributeNormalizationException { - CorrelationAttributeInstance corrAttr = null; - /* * Extract the phone number from the artifact attribute. */ @@ -305,56 +322,19 @@ public class CorrelationAttributeUtil { } else if (null != getAttribute(attributes, new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) { value = getAttribute(attributes, new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString(); } - /* * Normalize the phone number. */ if (value != null && CorrelationAttributeNormalizer.isValidPhoneNumber(value)) { - value = CorrelationAttributeNormalizer.normalizePhone(value); - corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value); + CorrelationAttributeInstance corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value); if (corrAttr != null) { corrAttrInstances.add(corrAttr); } } } - /** - * Gets the associated artifact of a "meta-artifact" such as an "interesting - * artifact hit" or "previously seen" artifact. - * - * @param artifact An artifact. - * - * @return The associated artifact if the input artifact is a - * "meta-artifact", otherwise the input artifact. - * - * @throws NoCurrentCaseException If there is no open case. - * @throws TskCoreException If there is an error querying thew case - * database. - */ - private static BlackboardArtifact getCorrAttrSourceArtifact(BlackboardArtifact artifact) throws NoCurrentCaseException, TskCoreException { - BlackboardArtifact sourceArtifact = null; - if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == artifact.getArtifactTypeID()) { - BlackboardAttribute assocArtifactAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); - if (assocArtifactAttr != null) { - sourceArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(assocArtifactAttr.getValueLong()); - } - } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PREVIOUSLY_SEEN.getTypeID() == artifact.getArtifactTypeID() - || BlackboardArtifact.ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE.getTypeID() == artifact.getArtifactTypeID() - || BlackboardArtifact.ARTIFACT_TYPE.TSK_PREVIOUSLY_UNSEEN.getTypeID() == artifact.getArtifactTypeID()) { - Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); - if (content instanceof DataArtifact) { - sourceArtifact = (BlackboardArtifact) content; - } - } - - if (sourceArtifact == null) { - sourceArtifact = artifact; - } - return sourceArtifact; - } - /** * Makes a correlation attribute instance for an account artifact. * @@ -506,21 +486,21 @@ public class CorrelationAttributeUtil { */ private static CorrelationAttributeInstance makeCorrAttr(BlackboardArtifact artifact, CorrelationAttributeInstance.Type correlationType, String value, Content sourceContent, Content dataSource) { + Content srcContent = sourceContent; + Content dataSrc = dataSource; try { - - if (sourceContent == null) { - sourceContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); + if (srcContent == null) { + srcContent = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID()); } - if (null == sourceContent) { + if (null == srcContent) { logger.log(Level.SEVERE, "Error creating artifact instance of type {0}. Failed to load content with ID: {1} associated with artifact with ID: {2}", new Object[]{correlationType.getDisplayName(), artifact.getObjectID(), artifact.getId()}); // NON-NLS return null; } - - if (dataSource == null) { - dataSource = sourceContent.getDataSource(); + if (dataSrc == null) { + dataSrc = srcContent.getDataSource(); } - if (dataSource == null) { + if (dataSrc == null) { logger.log(Level.SEVERE, "Error creating artifact instance of type {0}. Failed to load data source for content with ID: {1}", new Object[]{correlationType.getDisplayName(), artifact.getObjectID()}); // NON-NLS return null; @@ -532,24 +512,24 @@ public class CorrelationAttributeUtil { correlationType, value, correlationCase, - CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource), + CorrelationDataSource.fromTSKDataSource(correlationCase, dataSrc), "", "", TskData.FileKnown.UNKNOWN, - sourceContent.getId()); + srcContent.getId()); } else { - if (!(sourceContent instanceof AbstractFile)) { + if (!(srcContent instanceof AbstractFile)) { logger.log(Level.SEVERE, "Error creating artifact instance of type {0}. Source content of artifact with ID: {1} is not an AbstractFile", new Object[]{correlationType.getDisplayName(), artifact.getId()}); return null; } - AbstractFile bbSourceFile = (AbstractFile) sourceContent; + AbstractFile bbSourceFile = (AbstractFile) srcContent; return new CorrelationAttributeInstance( correlationType, value, correlationCase, - CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource), + CorrelationDataSource.fromTSKDataSource(correlationCase, dataSrc), bbSourceFile.getParentPath() + bbSourceFile.getName(), "", TskData.FileKnown.UNKNOWN, @@ -615,8 +595,12 @@ public class CorrelationAttributeUtil { } } + // @@@ BC: This seems like it should go into a DB-specific class because it is + // much different from the other methods in this class. It is going to the DB for data. /** - * Gets the correlation attribute instance for a file. + * Gets the correlation attribute instance for a file. This method goes to + * the CR to get an actual instance. It does not simply package the data + * from file into a generic instance object. * * @param file The file. * @@ -694,7 +678,8 @@ public class CorrelationAttributeUtil { } /** - * Makes a correlation attribute instance for a file. + * Makes a correlation attribute instance for a file. Will include the + * specific object ID. * * IMPORTANT: The correlation attribute instance is NOT added to the central * repository by this method. @@ -711,23 +696,23 @@ public class CorrelationAttributeUtil { * * @return The correlation attribute instance or null, if an error occurred. */ - public static CorrelationAttributeInstance makeCorrAttrFromFile(AbstractFile file) { - + private static List makeCorrAttrsForSearch(AbstractFile file) { + List fileTypeList = new ArrayList<>(); // will be an empty or single element list as was decided in 7852 if (!isSupportedAbstractFileType(file)) { - return null; + return fileTypeList; } // We need a hash to make the correlation artifact instance. String md5 = file.getMd5Hash(); if (md5 == null || md5.isEmpty() || HashUtility.isNoDataMd5(md5)) { - return null; + return fileTypeList; } try { CorrelationAttributeInstance.Type filesType = CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows()); - return new CorrelationAttributeInstance( + fileTypeList.add(new CorrelationAttributeInstance( filesType, file.getMd5Hash(), correlationCase, @@ -735,21 +720,17 @@ public class CorrelationAttributeUtil { file.getParentPath() + file.getName(), "", TskData.FileKnown.UNKNOWN, - file.getId()); - + file.getId())); } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Error querying case database (%s)", file), ex); // NON-NLS - return null; } catch (CentralRepoException ex) { logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS - return null; } catch (CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, String.format("Error creating correlation attribute instance (%s)", file), ex); // NON-NLS - return null; } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS - return null; } + return fileTypeList; } /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index 474ac1cd85..4fe5467bef 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -30,6 +30,7 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; +import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -65,8 +66,10 @@ import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OTHER_CASES; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_TYPE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_VALUE; import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccountInstance; import org.sleuthkit.datamodel.Score; @@ -311,18 +314,18 @@ public final class CaseEventListener implements PropertyChangeListener { * Sets the known status for the correlation attribute instance for the * given abstract file. * - * @param af The abstract file for which to set the correlation - * attribute instance. + * @param af The abstract file for which to set the correlation + * attribute instance. * @param knownStatus The new known status for the correlation attribute - * instance. + * instance. */ private void setContentKnownStatus(AbstractFile af, TskData.FileKnown knownStatus) { - final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile(af); - - if (eamArtifact != null) { + final List md5CorrelationAttr = CorrelationAttributeUtil.makeCorrAttrsForSearch(af); + if (!md5CorrelationAttr.isEmpty()) { + //for an abstract file the 'list' of attributes will be a single attribute or empty and is returning a list for consistancy with other makeCorrAttrsForSearch methods per 7852 // send update to Central Repository db try { - dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); + dbManager.setAttributeInstanceKnownStatus(md5CorrelationAttr.get(0), knownStatus); } catch (CentralRepoException ex) { LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS } @@ -407,7 +410,7 @@ public final class CaseEventListener implements PropertyChangeListener { * for the item. If there are, set known status as notable. If not set * status as unknown. * - * @param content The content for the tag that was added or deleted. + * @param content The content for the tag that was added or deleted. * @param bbArtifact The artifact for the tag that was added or deleted. */ private void handleTagChange(Content content, BlackboardArtifact bbArtifact) { @@ -452,11 +455,11 @@ public final class CaseEventListener implements PropertyChangeListener { * Sets the known status of a blackboard artifact in the central * repository. * - * @param bbArtifact The blackboard artifact to set known status. + * @param bbArtifact The blackboard artifact to set known status. * @param knownStatus The new known status. */ private void setArtifactKnownStatus(BlackboardArtifact bbArtifact, TskData.FileKnown knownStatus) { - List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact); + List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForSearch(bbArtifact); for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { try { dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); @@ -528,7 +531,7 @@ public final class CaseEventListener implements PropertyChangeListener { if (!hasTagWithConflictingKnownStatus) { //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed //with the initial set of correlation attributes this should be a single correlation attribute - List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbTag.getArtifact()); + List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForSearch(bbTag.getArtifact()); for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); } @@ -566,9 +569,10 @@ public final class CaseEventListener implements PropertyChangeListener { if (!hasTagWithConflictingKnownStatus) { Content taggedContent = contentTag.getContent(); if (taggedContent instanceof AbstractFile) { - final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile) taggedContent); - if (eamArtifact != null) { - CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); + final List eamArtifact = CorrelationAttributeUtil.makeCorrAttrsForSearch((AbstractFile) taggedContent); + if (!eamArtifact.isEmpty()) { + //for an abstract file the 'list' of attributes will be a single attribute or empty and is returning a list for consistancy with other makeCorrAttrsForSearch methods per 7852 + CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact.get(0), tagName.getKnownStatus()); } } } @@ -702,22 +706,45 @@ public final class CaseEventListener implements PropertyChangeListener { // Look up and create artifacts for previously seen accounts if requested if (IngestEventsListener.isFlagSeenDevices()) { - List previousOccurences = dbManager.getArtifactInstancesByTypeValue(CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID), correlationAttributeInstance.getCorrelationValue()); + CorrelationAttributeInstance.Type osAcctType = CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID); + List previousOccurences = dbManager.getArtifactInstancesByTypeValue(osAcctType, correlationAttributeInstance.getCorrelationValue()); for (CorrelationAttributeInstance instance : previousOccurences) { if (!instance.getCorrelationCase().getCaseUUID().equals(correlationAttributeInstance.getCorrelationCase().getCaseUUID())) { SleuthkitCase tskCase = osAccount.getSleuthkitCase(); Blackboard blackboard = tskCase.getBlackboard(); + List caseDisplayNames = dbManager.getListCasesHavingArtifactInstances(osAcctType, correlationAttributeInstance.getCorrelationValue()); + + // calculate score + Score score; + int numCases = caseDisplayNames.size(); + if (numCases <= IngestEventsListener.MAX_NUM_PREVIOUS_CASES_FOR_LIKELY_NOTABLE_SCORE) { + score = Score.SCORE_LIKELY_NOTABLE; + } else if (numCases > IngestEventsListener.MAX_NUM_PREVIOUS_CASES_FOR_LIKELY_NOTABLE_SCORE && numCases <= IngestEventsListener.MAX_NUM_PREVIOUS_CASES_FOR_PREV_SEEN_ARTIFACT_CREATION) { + score = Score.SCORE_NONE; + } else { + // don't make an Analysis Result, the artifact is too common. + continue; + } + + String prevCases = caseDisplayNames.stream().distinct().collect(Collectors.joining(",")); + String justification = "Previously seen in cases " + prevCases; Collection attributesForNewArtifact = Arrays.asList( new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, Bundle.CaseEventsListener_prevExists_text()), new BlackboardAttribute( - TSK_COMMENT, MODULE_NAME, - Bundle.CaseEventsListener_prevCaseComment_text())); + TSK_CORRELATION_TYPE, MODULE_NAME, + osAcctType.getDisplayName()), + new BlackboardAttribute( + TSK_CORRELATION_VALUE, MODULE_NAME, + correlationAttributeInstance.getCorrelationValue()), + new BlackboardAttribute( + TSK_OTHER_CASES, MODULE_NAME, + prevCases)); BlackboardArtifact newAnalysisResult = osAccount.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, Score.SCORE_LIKELY_NOTABLE, - null, Bundle.CaseEventsListener_prevExists_text(), null, attributesForNewArtifact, osAccountInstance.getDataSource().getId()).getAnalysisResult(); + BlackboardArtifact.Type.TSK_PREVIOUSLY_SEEN, score, + null, Bundle.CaseEventsListener_prevExists_text(), justification, attributesForNewArtifact, osAccountInstance.getDataSource().getId()).getAnalysisResult(); try { // index the artifact for keyword search blackboard.postArtifact(newAnalysisResult, MODULE_NAME); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 7aa229949e..cd9e03c9aa 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2017-2020 Basis Technology Corp. + * Copyright 2017-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -46,16 +47,15 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.autopsy.coreutils.ThreadUtils; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_TYPE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_VALUE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OTHER_CASES; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisEvent; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Image; @@ -63,6 +63,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.datamodel.Score; +import org.sleuthkit.datamodel.TskData; /** * Listen for ingest events and update entries in the Central Repository @@ -70,6 +71,7 @@ import org.sleuthkit.datamodel.Score; */ @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"}) public class IngestEventsListener { + private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private static final Set INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED); @@ -78,11 +80,15 @@ public class IngestEventsListener { private static boolean flagNotableItems; private static boolean flagSeenDevices; private static boolean createCrProperties; + private static boolean flagUniqueArtifacts; private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d"; private final ExecutorService jobProcessingExecutor; private final PropertyChangeListener pcl1 = new IngestModuleEventListener(); private final PropertyChangeListener pcl2 = new IngestJobEventListener(); final Collection recentlyAddedCeArtifacts = new LinkedHashSet<>(); + + static final int MAX_NUM_PREVIOUS_CASES_FOR_LIKELY_NOTABLE_SCORE = 10; + static final int MAX_NUM_PREVIOUS_CASES_FOR_PREV_SEEN_ARTIFACT_CREATION = 20; public IngestEventsListener() { jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(INGEST_EVENT_THREAD_NAME).build()); @@ -188,6 +194,24 @@ public class IngestEventsListener { public synchronized static void setFlagSeenDevices(boolean value) { flagSeenDevices = value; } + + /** + * Configure the listener to flag unique apps or not. + * + * @param value True to flag unique apps; otherwise false. + */ + public synchronized static void setFlagUniqueArtifacts(boolean value) { + flagUniqueArtifacts = value; + } + + /** + * Are unique apps being flagged? + * + * @return True if flagging unique apps; otherwise false. + */ + public synchronized static boolean isFlagUniqueArtifacts() { + return flagUniqueArtifacts; + } /** * Configure the listener to create correlation properties @@ -199,7 +223,7 @@ public class IngestEventsListener { } /** - * Make an Interesting Item artifact based on a new artifact being + * Make a "previously seen" artifact based on a new artifact being * previously seen. * * @param originalArtifact Original artifact that we want to flag @@ -208,25 +232,31 @@ public class IngestEventsListener { */ @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", "IngestEventsListener.prevCaseComment.text=Previous Case: "}) - static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List caseDisplayNames) { - Collection attributesForNewArtifact = Arrays.asList( + static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List caseDisplayNames, + CorrelationAttributeInstance.Type aType, String value) { + String prevCases = caseDisplayNames.stream().distinct().collect(Collectors.joining(",")); + String justification = "Previously marked as notable in cases " + prevCases; + Collection attributesForNewArtifact = Arrays.asList(new BlackboardAttribute( + TSK_SET_NAME, MODULE_NAME, + Bundle.IngestEventsListener_prevTaggedSet_text()), new BlackboardAttribute( - TSK_SET_NAME, MODULE_NAME, - Bundle.IngestEventsListener_prevTaggedSet_text()), + TSK_CORRELATION_TYPE, MODULE_NAME, + aType.getDisplayName()), new BlackboardAttribute( - TSK_COMMENT, MODULE_NAME, - Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))), + TSK_CORRELATION_VALUE, MODULE_NAME, + value), new BlackboardAttribute( - TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, - originalArtifact.getArtifactID())); - makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevTaggedSet_text()); + TSK_OTHER_CASES, MODULE_NAME, + prevCases)); + makeAndPostArtifact(BlackboardArtifact.Type.TSK_PREVIOUSLY_NOTABLE, originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevTaggedSet_text(), + Score.SCORE_NOTABLE, justification); } /** - * Create an Interesting Artifact hit for a device which was previously seen - * in the central repository. + * Create a "previously seen" hit for a device which was previously seen + * in the central repository. NOTE: Artifacts that are too common will be skipped. * - * @param originalArtifact the artifact to create the interesting item for + * @param originalArtifact the artifact to create the "previously seen" item for * @param caseDisplayNames the case names the artifact was previously seen * in */ @@ -234,45 +264,85 @@ public class IngestEventsListener { "# {0} - typeName", "# {1} - count", "IngestEventsListener.prevCount.text=Number of previous {0}: {1}"}) - static private void makeAndPostPreviousSeenArtifact(BlackboardArtifact originalArtifact, List caseDisplayNames) { + static private void makeAndPostPreviousSeenArtifact(BlackboardArtifact originalArtifact, List caseDisplayNames, + CorrelationAttributeInstance.Type aType, String value) { + + // calculate score + Score score; + int numCases = caseDisplayNames.size(); + if (numCases <= MAX_NUM_PREVIOUS_CASES_FOR_LIKELY_NOTABLE_SCORE) { + score = Score.SCORE_LIKELY_NOTABLE; + } else if (numCases > MAX_NUM_PREVIOUS_CASES_FOR_LIKELY_NOTABLE_SCORE && numCases <= MAX_NUM_PREVIOUS_CASES_FOR_PREV_SEEN_ARTIFACT_CREATION) { + score = Score.SCORE_NONE; + } else { + // don't make an Analysis Result, the artifact is too common. + return; + } + + String prevCases = caseDisplayNames.stream().distinct().collect(Collectors.joining(",")); + String justification = "Previously seen in cases " + prevCases; Collection attributesForNewArtifact = Arrays.asList(new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, Bundle.IngestEventsListener_prevExists_text()), new BlackboardAttribute( - TSK_COMMENT, MODULE_NAME, - Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))), + TSK_CORRELATION_TYPE, MODULE_NAME, + aType.getDisplayName()), new BlackboardAttribute( - TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, - originalArtifact.getArtifactID())); - makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevExists_text()); + TSK_CORRELATION_VALUE, MODULE_NAME, + value), + new BlackboardAttribute( + TSK_OTHER_CASES, MODULE_NAME, + prevCases)); + makeAndPostArtifact(BlackboardArtifact.Type.TSK_PREVIOUSLY_SEEN, originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevExists_text(), + score, justification); } + /** + * Create a "previously unseen" hit for an application which was never seen in + * the central repository. + * + * @param originalArtifact the artifact to create the "previously unseen" item + * for + */ + static private void makeAndPostPreviouslyUnseenArtifact(BlackboardArtifact originalArtifact, CorrelationAttributeInstance.Type aType, String value) { + Collection attributesForNewArtifact = Arrays.asList( + new BlackboardAttribute( + TSK_CORRELATION_TYPE, MODULE_NAME, + aType.getDisplayName()), + new BlackboardAttribute( + TSK_CORRELATION_VALUE, MODULE_NAME, + value)); + makeAndPostArtifact(BlackboardArtifact.Type.TSK_PREVIOUSLY_UNSEEN, originalArtifact, attributesForNewArtifact, "", + Score.SCORE_LIKELY_NOTABLE, "This application has not been previously seen before"); + } /** - * Make an interesting item artifact to flag the passed in artifact. + * Make an artifact to flag the passed in artifact. * * @param originalArtifact Artifact in current case we want to flag - * @param attributesForNewArtifact Attributes to assign to the new - * Interesting items artifact - * @param configuration The configuration to be specified for the new interesting artifact hit + * @param attributesForNewArtifact Attributes to assign to the new artifact + * @param configuration The configuration to be specified for the new artifact hit + * @param score sleuthkit.datamodel.Score to be assigned to this artifact + * @param justification Justification string */ - private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection attributesForNewArtifact, String configuration) { + private static void makeAndPostArtifact(BlackboardArtifact.Type newArtifactType, BlackboardArtifact originalArtifact, Collection attributesForNewArtifact, String configuration, + Score score, String justification) { try { SleuthkitCase tskCase = originalArtifact.getSleuthkitCase(); - AbstractFile abstractFile = tskCase.getAbstractFileById(originalArtifact.getObjectID()); Blackboard blackboard = tskCase.getBlackboard(); // Create artifact if it doesn't already exist. - if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) { - BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, Score.SCORE_LIKELY_NOTABLE, - null, configuration, null, attributesForNewArtifact) + BlackboardArtifact.ARTIFACT_TYPE type = BlackboardArtifact.ARTIFACT_TYPE.fromID(newArtifactType.getTypeID()); + if (!blackboard.artifactExists(originalArtifact, type, attributesForNewArtifact)) { + BlackboardArtifact newArtifact = originalArtifact.newAnalysisResult( + newArtifactType, score, + null, configuration, justification, attributesForNewArtifact) .getAnalysisResult(); try { // index the artifact for keyword search - blackboard.postArtifact(newInterestingArtifact, MODULE_NAME); + blackboard.postArtifact(newArtifact, MODULE_NAME); } catch (Blackboard.BlackboardException ex) { - LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + newInterestingArtifact.getArtifactID(), ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + newArtifact.getArtifactID(), ex); //NON-NLS } } } catch (TskCoreException ex) { @@ -299,11 +369,13 @@ public class IngestEventsListener { } switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { case DATA_ADDED: { - //if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items + //if ingest isn't running create the "previously seen" items, + // otherwise use the ingest module setting to determine if we create "previously seen" items boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems(); boolean flagPrevious = !IngestManager.getInstance().isIngestRunning() || isFlagSeenDevices(); boolean createAttributes = !IngestManager.getInstance().isIngestRunning() || shouldCreateCrProperties(); - jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious, createAttributes)); + boolean flagUnique = !IngestManager.getInstance().isIngestRunning() || isFlagUniqueArtifacts(); + jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious, createAttributes, flagUnique)); break; } default: @@ -368,8 +440,8 @@ public class IngestEventsListener { try { dataSource = ((DataSourceAnalysisEvent) event).getDataSource(); /* - * We only care about Images for the purpose of - * updating hash values. + * We only care about Images for the purpose of updating hash + * values. */ if (!(dataSource instanceof Image)) { return; @@ -443,13 +515,15 @@ public class IngestEventsListener { private final boolean flagNotableItemsEnabled; private final boolean flagPreviousItemsEnabled; private final boolean createCorrelationAttributes; + private final boolean flagUniqueItemsEnabled; - private DataAddedTask(CentralRepository db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled, boolean createCorrelationAttributes) { + private DataAddedTask(CentralRepository db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled, boolean createCorrelationAttributes, boolean flagUnique) { this.dbManager = db; this.event = evt; this.flagNotableItemsEnabled = flagNotableItemsEnabled; this.flagPreviousItemsEnabled = flagPreviousItemsEnabled; this.createCorrelationAttributes = createCorrelationAttributes; + this.flagUniqueItemsEnabled = flagUnique; } @Override @@ -471,41 +545,63 @@ public class IngestEventsListener { try { // Only do something with this artifact if it's unique within the job if (recentlyAddedCeArtifacts.add(eamArtifact.toString())) { + + // Get a list of instances for a given value (hash, email, etc.) + List previousOccurrences = new ArrayList<>(); + // check if we are flagging things + if (flagNotableItemsEnabled || flagPreviousItemsEnabled || flagUniqueItemsEnabled) { + try { + previousOccurrences = dbManager.getArtifactInstancesByTypeValue(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); + + // make sure the previous instances do not contain current case + for (Iterator iterator = previousOccurrences.iterator(); iterator.hasNext();) { + CorrelationAttributeInstance instance = iterator.next(); + if (instance.getCorrelationCase().getCaseUUID().equals(eamArtifact.getCorrelationCase().getCaseUUID())) { + // this is the current case - remove the instace from the previousOccurrences list + iterator.remove(); + } + } + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, String.format("Unable to flag previously seen device: %s.", eamArtifact.toString()), ex); + } + } + // Was it previously marked as bad? // query db for artifact instances having this TYPE/VALUE and knownStatus = "Bad". // if getKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case, - // create TSK_INTERESTING_ARTIFACT_HIT artifact on BB. + // create TSK_PREVIOUSLY_SEEN artifact on BB. if (flagNotableItemsEnabled) { - List caseDisplayNames; - try { - caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); - if (!caseDisplayNames.isEmpty()) { - makeAndPostPreviousNotableArtifact(bbArtifact, - caseDisplayNames); - } - } catch (CorrelationAttributeNormalizationException ex) { - LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex); + List caseDisplayNames = getCaseDisplayNamesForNotable(previousOccurrences); + if (!caseDisplayNames.isEmpty()) { + makeAndPostPreviousNotableArtifact(bbArtifact, + caseDisplayNames, eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); + + // if we have marked this artifact as notable, then skip the analysis of whether it was previously seen + continue; } } - if (flagPreviousItemsEnabled + + // flag previously seen devices and communication accounts (emails, phones, etc) + if (flagPreviousItemsEnabled && !previousOccurrences.isEmpty() && (eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.USBID_TYPE_ID || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.ICCID_TYPE_ID || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMEI_TYPE_ID || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMSI_TYPE_ID - || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.MAC_TYPE_ID)) { - try { - //only alert to previous instances when they were in another case - List previousOccurences = dbManager.getArtifactInstancesByTypeValue(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); - List caseDisplayNames; - for (CorrelationAttributeInstance instance : previousOccurences) { - if (!instance.getCorrelationCase().getCaseUUID().equals(eamArtifact.getCorrelationCase().getCaseUUID())) { - caseDisplayNames = dbManager.getListCasesHavingArtifactInstances(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); - makeAndPostPreviousSeenArtifact(bbArtifact, caseDisplayNames); - break; - } - } - } catch (CorrelationAttributeNormalizationException ex) { - LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex); + || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.MAC_TYPE_ID + || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID + || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.PHONE_TYPE_ID)) { + + List caseDisplayNames = getCaseDisplayNames(previousOccurrences); + makeAndPostPreviousSeenArtifact(bbArtifact, caseDisplayNames, eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); + } + + // flag previously unseen apps and domains + if (flagUniqueItemsEnabled + && (eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID + || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.DOMAIN_TYPE_ID)) { + + if (previousOccurrences.isEmpty()) { + makeAndPostPreviouslyUnseenArtifact(bbArtifact, eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue()); } } if (createCorrelationAttributes) { @@ -528,4 +624,36 @@ public class IngestEventsListener { } // DATA_ADDED } } + + /** + * Gets case display names for a list of CorrelationAttributeInstance. + * + * @param occurrences List of CorrelationAttributeInstance + * + * @return List of case display names + */ + private List getCaseDisplayNames(List occurrences) { + List caseNames = new ArrayList<>(); + for (CorrelationAttributeInstance occurrence : occurrences) { + caseNames.add(occurrence.getCorrelationCase().getDisplayName()); + } + return caseNames; + } + + /** + * Gets case display names for only occurrences marked as NOTABLE/BAD. + * + * @param occurrences List of CorrelationAttributeInstance + * + * @return List of case display names of NOTABLE/BAD occurrences + */ + private List getCaseDisplayNamesForNotable(List occurrences) { + List caseNames = new ArrayList<>(); + for (CorrelationAttributeInstance occurrence : occurrences) { + if (occurrence.getKnownStatus() == TskData.FileKnown.BAD) { + caseNames.add(occurrence.getCorrelationCase().getDisplayName()); + } + } + return caseNames; + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties index 88832acd4b..b00d4d5540 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties @@ -2,3 +2,4 @@ IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices and users previously seen in other cases IngestSettingsPanel.createCorrelationPropertiesCheckbox.text=Save items to the Central Repository +IngestSettingsPanel.flagUniqueAppsCheckbox.text=Flag apps and domains not seen in other cases diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED index c5ac5ed8db..d15eb90cb2 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/Bundle.properties-MERGED @@ -13,3 +13,4 @@ IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices and users previously seen in other cases IngestSettingsPanel.createCorrelationPropertiesCheckbox.text=Save items to the Central Repository +IngestSettingsPanel.flagUniqueAppsCheckbox.text=Flag apps and domains not seen in other cases diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index d40b45be86..49be6d3d6b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,10 +48,12 @@ import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE; import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_TYPE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_VALUE; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OTHER_CASES; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -69,6 +71,7 @@ final class CentralRepoIngestModule implements FileIngestModule { private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false; + static final boolean DEFAULT_FLAG_UNIQUE_DEVICES = false; static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName()); @@ -83,6 +86,7 @@ final class CentralRepoIngestModule implements FileIngestModule { private final boolean flagPreviouslySeenDevices; private Blackboard blackboard; private final boolean createCorrelationProperties; + private final boolean flagUniqueArtifacts; /** * Instantiate the Central Repository ingest module. @@ -93,6 +97,7 @@ final class CentralRepoIngestModule implements FileIngestModule { flagTaggedNotableItems = settings.isFlagTaggedNotableItems(); flagPreviouslySeenDevices = settings.isFlagPreviousDevices(); createCorrelationProperties = settings.shouldCreateCorrelationProperties(); + flagUniqueArtifacts = settings.isFlagUniqueArtifacts(); } @Override @@ -151,7 +156,7 @@ final class CentralRepoIngestModule implements FileIngestModule { List caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5); HealthMonitor.submitTimingMetric(timingMetric); if (!caseDisplayNamesList.isEmpty()) { - postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList); + postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList, filesType, md5); } } catch (CentralRepoException ex) { logger.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS @@ -249,6 +254,9 @@ final class CentralRepoIngestModule implements FileIngestModule { if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.shouldCreateCrProperties()) { IngestEventsListener.setCreateCrProperties(createCorrelationProperties); } + if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagUniqueArtifacts()) { + IngestEventsListener.setFlagUniqueArtifacts(flagUniqueArtifacts); + } if (CentralRepository.isEnabled() == false) { /* @@ -327,26 +335,33 @@ final class CentralRepoIngestModule implements FileIngestModule { } /** - * Post a new interesting artifact for the file marked bad. + * Post a new "previously seen" artifact for the file marked bad. * * @param abstractFile The file from which to create an artifact. * @param caseDisplayNames Case names to be added to a TSK_COMMON attribute. */ - private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List caseDisplayNames) { + private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List caseDisplayNames, CorrelationAttributeInstance.Type aType, String value) { + String prevCases = caseDisplayNames.stream().distinct().collect(Collectors.joining(",")); + String justification = "Previously marked as notable in cases " + prevCases; Collection attributes = Arrays.asList( new BlackboardAttribute( TSK_SET_NAME, MODULE_NAME, Bundle.CentralRepoIngestModule_prevTaggedSet_text()), new BlackboardAttribute( - TSK_COMMENT, MODULE_NAME, - Bundle.CentralRepoIngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",")))); + TSK_CORRELATION_TYPE, MODULE_NAME, + aType.getDisplayName()), + new BlackboardAttribute( + TSK_CORRELATION_VALUE, MODULE_NAME, + value), + new BlackboardAttribute( + TSK_OTHER_CASES, MODULE_NAME, + prevCases)); try { - // Create artifact if it doesn't already exist. - if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) { + if (!blackboard.artifactExists(abstractFile, TSK_PREVIOUSLY_NOTABLE, attributes)) { BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult( - BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, Score.SCORE_LIKELY_NOTABLE, - null, Bundle.CentralRepoIngestModule_prevTaggedSet_text(), null, attributes) + BlackboardArtifact.Type.TSK_PREVIOUSLY_NOTABLE, Score.SCORE_NOTABLE, + null, Bundle.CentralRepoIngestModule_prevTaggedSet_text(), justification, attributes) .getAnalysisResult(); try { // index the artifact for keyword search diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModuleFactory.java index 39c80abefc..22ce22f65f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModuleFactory.java @@ -121,14 +121,14 @@ public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter { throw new IllegalArgumentException("Expected settings argument to be an instance of IngestSettings"); } - @Override - public boolean isDataArtifactIngestModuleFactory() { - return true; - } - - @Override - public DataArtifactIngestModule createDataArtifactIngestModule(IngestModuleIngestJobSettings settings) { - return new CentralRepoDataArtifactIngestModule(); - } +// @Override +// public boolean isDataArtifactIngestModuleFactory() { +// return true; +// } +// +// @Override +// public DataArtifactIngestModule createDataArtifactIngestModule(IngestModuleIngestJobSettings settings) { +// return new CentralRepoDataArtifactIngestModule(); +// } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java index 52d645bcac..e55f09882e 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettings.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ final class IngestSettings implements IngestModuleIngestJobSettings { private final boolean flagTaggedNotableItems; private final boolean flagPreviousDevices; private final boolean createCorrelationProperties; + private final boolean flagUniqueArtifacts; /** * Instantiate the ingest job settings with default values. @@ -38,6 +39,7 @@ final class IngestSettings implements IngestModuleIngestJobSettings { this.flagTaggedNotableItems = CentralRepoIngestModule.DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS; this.flagPreviousDevices = CentralRepoIngestModule.DEFAULT_FLAG_PREVIOUS_DEVICES; this.createCorrelationProperties = CentralRepoIngestModule.DEFAULT_CREATE_CR_PROPERTIES; + this.flagUniqueArtifacts = CentralRepoIngestModule.DEFAULT_FLAG_UNIQUE_DEVICES; } /** @@ -48,11 +50,14 @@ final class IngestSettings implements IngestModuleIngestJobSettings { * the Central Repository * @param createCorrelationProperties Create correlation properties in the * central repository + * @param flagUniqueArtifacts Flag unique artifacts that have not + * been seen in any other cases */ - IngestSettings(boolean flagTaggedNotableItems, boolean flagPreviousDevices, boolean createCorrelationProperties) { + IngestSettings(boolean flagTaggedNotableItems, boolean flagPreviousDevices, boolean createCorrelationProperties, boolean flagUniqueArtifacts) { this.flagTaggedNotableItems = flagTaggedNotableItems; this.flagPreviousDevices = flagPreviousDevices; this.createCorrelationProperties = createCorrelationProperties; + this.flagUniqueArtifacts = flagUniqueArtifacts; } @Override @@ -86,4 +91,14 @@ final class IngestSettings implements IngestModuleIngestJobSettings { boolean shouldCreateCorrelationProperties() { return createCorrelationProperties; } + + /** + * Are artifacts (apps, domains) previously unseen in other cases to be + * flagged? + * + * @return True if flagging; otherwise false. + */ + public boolean isFlagUniqueArtifacts() { + return flagUniqueArtifacts; + } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form index b685d08432..102a2635a0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.form @@ -26,10 +26,11 @@ + - + @@ -44,7 +45,9 @@ - + + + @@ -83,5 +86,12 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java index 2804cf1ce7..5d54bd0f81 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,11 +44,13 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { flagTaggedNotableItemsCheckbox.setSelected(settings.isFlagTaggedNotableItems()); flagPreviouslySeenDevicesCheckbox.setSelected(settings.isFlagPreviousDevices()); createCorrelationPropertiesCheckbox.setSelected(settings.shouldCreateCorrelationProperties()); + flagUniqueAppsCheckbox.setSelected(settings.isFlagUniqueArtifacts()); } @Override public IngestModuleIngestJobSettings getSettings() { - return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected(), flagPreviouslySeenDevicesCheckbox.isSelected(), createCorrelationPropertiesCheckbox.isSelected()); + return new IngestSettings(flagTaggedNotableItemsCheckbox.isSelected(), flagPreviouslySeenDevicesCheckbox.isSelected(), + createCorrelationPropertiesCheckbox.isSelected(), flagUniqueAppsCheckbox.isSelected()); } /** @@ -64,6 +66,7 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { flagTaggedNotableItemsCheckbox = new javax.swing.JCheckBox(); flagPreviouslySeenDevicesCheckbox = new javax.swing.JCheckBox(); createCorrelationPropertiesCheckbox = new javax.swing.JCheckBox(); + flagUniqueAppsCheckbox = new javax.swing.JCheckBox(); ingestSettingsLabel.setFont(ingestSettingsLabel.getFont().deriveFont(ingestSettingsLabel.getFont().getStyle() | java.awt.Font.BOLD)); org.openide.awt.Mnemonics.setLocalizedText(ingestSettingsLabel, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.ingestSettingsLabel.text")); // NOI18N @@ -74,6 +77,8 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { org.openide.awt.Mnemonics.setLocalizedText(createCorrelationPropertiesCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.createCorrelationPropertiesCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(flagUniqueAppsCheckbox, org.openide.util.NbBundle.getMessage(IngestSettingsPanel.class, "IngestSettingsPanel.flagUniqueAppsCheckbox.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -87,8 +92,9 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(flagTaggedNotableItemsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(flagPreviouslySeenDevicesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(createCorrelationPropertiesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(createCorrelationPropertiesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(flagUniqueAppsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap(16, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -101,7 +107,9 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { .addComponent(flagTaggedNotableItemsCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(flagPreviouslySeenDevicesCheckbox) - .addContainerGap(47, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(flagUniqueAppsCheckbox) + .addContainerGap(24, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -109,6 +117,7 @@ final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { private javax.swing.JCheckBox createCorrelationPropertiesCheckbox; private javax.swing.JCheckBox flagPreviouslySeenDevicesCheckbox; private javax.swing.JCheckBox flagTaggedNotableItemsCheckbox; + private javax.swing.JCheckBox flagUniqueAppsCheckbox; private javax.swing.JLabel ingestSettingsLabel; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index d17c1766d6..be12157474 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -15,9 +15,6 @@ # governing permissions and limitations under the License. # -AnnotationsContentViewer.onEmpty=No annotations were found for this particular item. -AnnotationsContentViewer.title=Annotations -AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java index ef623f44cf..d40f6c2a39 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsContentViewer.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.contentviewers.analysisresults; import java.awt.Component; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingWorker; @@ -28,24 +29,21 @@ import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.datamodel.TskContentItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; -import org.sleuthkit.datamodel.AnalysisResult; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.autopsy.datamodel.AnalysisResultItem; import org.sleuthkit.datamodel.TskCoreException; /** - * Displays a list of analysis results as a content viewer. + * A content viewer that displays the analysis results for a Content object. */ @ServiceProvider(service = DataContentViewer.class, position = 7) public class AnalysisResultsContentViewer implements DataContentViewer { private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName()); - - // isPreferred value private static final int PREFERRED_VALUE = 3; - private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel(); private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel(); @@ -125,26 +123,24 @@ public class AnalysisResultsContentViewer implements DataContentViewer { @Override public boolean isSupported(Node node) { - if (node == null) { + if (Objects.isNull(node)) { return false; } - // There needs to either be a file with an AnalysisResult or an AnalysisResult in the lookup. - for (Content content : node.getLookup().lookupAll(Content.class)) { - if (content instanceof AnalysisResult) { - return true; - } - - if (content == null || content instanceof BlackboardArtifact) { - continue; - } + AnalysisResultItem analysisResultItem = node.getLookup().lookup(AnalysisResultItem.class); + if (Objects.nonNull(analysisResultItem)) { + return true; + } + TskContentItem contentItem = node.getLookup().lookup(TskContentItem.class); + if (!Objects.isNull(contentItem)) { + Content content = contentItem.getTskContent(); try { if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) { return true; } } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex); + logger.log(Level.SEVERE, String.format("Error getting analysis results for Content (object ID = %d)", content.getId()), ex); } } @@ -155,4 +151,5 @@ public class AnalysisResultsContentViewer implements DataContentViewer { public int isPreferred(Node node) { return PREFERRED_VALUE; } + } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java index 03720e0cbe..b470f9dfb7 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/AnalysisResultsViewModel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2021 Basis Technology Corp. + * Copyright 2021-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,9 +20,8 @@ package org.sleuthkit.autopsy.contentviewers.analysisresults; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -31,14 +30,14 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Node; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.datamodel.AnalysisResultItem; +import org.sleuthkit.autopsy.datamodel.TskContentItem; import org.sleuthkit.datamodel.AnalysisResult; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.TskCoreException; /** - * * Creates a representation of a list of analysis results gathered from a node. */ public class AnalysisResultsViewModel { @@ -72,7 +71,7 @@ public class AnalysisResultsViewModel { * @return The attributes to display. */ List> getAttributesToDisplay() { - return attributesToDisplay; + return Collections.unmodifiableList(attributesToDisplay); } /** @@ -118,7 +117,7 @@ public class AnalysisResultsViewModel { * @return The analysis results to be displayed. */ List getAnalysisResults() { - return analysisResults; + return Collections.unmodifiableList(analysisResults); } /** @@ -238,58 +237,44 @@ public class AnalysisResultsViewModel { return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty(), Optional.empty()); } - Optional aggregateScore = Optional.empty(); - Optional nodeContent = Optional.empty(); - // maps id of analysis result to analysis result to prevent duplication - Map allAnalysisResults = new HashMap<>(); - Optional selectedResult = Optional.empty(); - - // Find first content that is not an artifact within node - for (Content content : node.getLookup().lookupAll(Content.class)) { - if (content == null || content instanceof BlackboardArtifact) { - continue; - } - - try { - nodeContent = Optional.of(content); - - // get the aggregate score of that content - aggregateScore = Optional.ofNullable(content.getAggregateScore()); - - // and add all analysis results to mapping - content.getAllAnalysisResults().stream() - .forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); - - break; - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex); + Content analyzedContent = null; + AnalysisResult selectedAnalysisResult = null; + Score aggregateScore = null; + List analysisResults = Collections.emptyList(); + long selectedObjectId = 0; + try { + AnalysisResultItem analysisResultItem = node.getLookup().lookup(AnalysisResultItem.class); + if (Objects.nonNull(analysisResultItem)) { + /* + * The content represented by the Node is an analysis result. + * Set this analysis result as the analysis result to be + * selected in the content viewer and get the analyzed content + * as the source of the analysis results to display. + */ + selectedAnalysisResult = analysisResultItem.getTskContent(); + selectedObjectId = selectedAnalysisResult.getId(); + analyzedContent = selectedAnalysisResult.getParent(); + } else { + /* + * The content represented by the Node is something other than + * an analysis result. Use it as the source of the analysis + * results to display. + */ + TskContentItem contentItem = node.getLookup().lookup(TskContentItem.class); + analyzedContent = contentItem.getTskContent(); + selectedObjectId = analyzedContent.getId(); } + aggregateScore = analyzedContent.getAggregateScore(); + analysisResults = analyzedContent.getAllAnalysisResults(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error getting analysis result data for selected Content (object ID=%d)", selectedObjectId), ex); } - // Find any analysis results in the node - Collection analysisResults = node.getLookup().lookupAll(AnalysisResult.class); - if (analysisResults.size() > 0) { - - // get any items with a score - List filteredResults = analysisResults.stream() - .collect(Collectors.toList()); - - // add them to the map to display - filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar)); - - // the selected result will be the highest scored analysis result in the node. - selectedResult = filteredResults.stream() - .max((a, b) -> a.getScore().compareTo(b.getScore())); - - // if no aggregate score determined at this point, use the selected result score. - if (!aggregateScore.isPresent()) { - aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore())); - } - } - - // get view model representation - List displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values()); - - return new NodeResults(displayAttributes, selectedResult, aggregateScore, nodeContent); + /* + * Use the data collected above to construct the view model. + */ + List displayAttributes = getOrderedDisplayAttributes(analysisResults); + return new NodeResults(displayAttributes, Optional.ofNullable(selectedAnalysisResult), Optional.ofNullable(aggregateScore), Optional.ofNullable(analyzedContent)); } + } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED index f4558602d8..ae4d0b3b6b 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/analysisresults/Bundle.properties-MERGED @@ -1,4 +1,5 @@ AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score +AnalysisResultsContentPanel_content_displayKey=Item # {0} - analysisResultsNumber AnalysisResultsContentPanel_result_headerKey=Analysis Result {0} AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Annotations.java b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationUtils.java similarity index 90% rename from Core/src/org/sleuthkit/autopsy/contentviewers/application/Annotations.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationUtils.java index 67124d1502..3a3e853501 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Annotations.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationUtils.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.contentviewers.application; +package org.sleuthkit.autopsy.contentviewers.annotations; import java.util.ArrayList; import java.util.Arrays; @@ -53,76 +53,76 @@ import org.sleuthkit.datamodel.TskCoreException; /** * The business logic for the Annotations content panel. */ -public class Annotations { +public class AnnotationUtils { @NbBundle.Messages({ - "Annotations.title=Annotations", - "Annotations.toolTip=Displays tags and comments associated with the selected content.", - "Annotations.centralRepositoryEntry.title=Central Repository Comments", - "Annotations.centralRepositoryEntryDataLabel.case=Case:", - "Annotations.centralRepositoryEntryDataLabel.type=Type:", - "Annotations.centralRepositoryEntryDataLabel.comment=Comment:", - "Annotations.centralRepositoryEntryDataLabel.path=Path:", - "Annotations.tagEntry.title=Tags", - "Annotations.tagEntryDataLabel.tag=Tag:", - "Annotations.tagEntryDataLabel.tagUser=Examiner:", - "Annotations.tagEntryDataLabel.comment=Comment:", - "Annotations.fileHitEntry.artifactCommentTitle=Artifact Comment", - "Annotations.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments", - "Annotations.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments", - "Annotations.fileHitEntry.setName=Set Name:", - "Annotations.fileHitEntry.comment=Comment:", - "Annotations.sourceFile.title=Source File", - "Annotations.onEmpty=No annotations were found for this particular item." + "AnnotationUtils.title=Annotations", + "AnnotationUtils.toolTip=Displays tags and comments associated with the selected content.", + "AnnotationUtils.centralRepositoryEntry.title=Central Repository Comments", + "AnnotationUtils.centralRepositoryEntryDataLabel.case=Case:", + "AnnotationUtils.centralRepositoryEntryDataLabel.type=Type:", + "AnnotationUtils.centralRepositoryEntryDataLabel.comment=Comment:", + "AnnotationUtils.centralRepositoryEntryDataLabel.path=Path:", + "AnnotationUtils.tagEntry.title=Tags", + "AnnotationUtils.tagEntryDataLabel.tag=Tag:", + "AnnotationUtils.tagEntryDataLabel.tagUser=Examiner:", + "AnnotationUtils.tagEntryDataLabel.comment=Comment:", + "AnnotationUtils.fileHitEntry.artifactCommentTitle=Artifact Comment", + "AnnotationUtils.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments", + "AnnotationUtils.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments", + "AnnotationUtils.fileHitEntry.setName=Set Name:", + "AnnotationUtils.fileHitEntry.comment=Comment:", + "AnnotationUtils.sourceFile.title=Source File", + "AnnotationUtils.onEmpty=No annotations were found for this particular item." }) - private static final Logger logger = Logger.getLogger(Annotations.class.getName()); + private static final Logger logger = Logger.getLogger(AnnotationUtils.class.getName()); private static final String EMPTY_HTML = ""; // describing table values for a tag private static final List> TAG_ENTRIES = Arrays.asList( - new ItemEntry<>(Bundle.Annotations_tagEntryDataLabel_tag(), + new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_tag(), (tag) -> (tag.getName() != null) ? tag.getName().getDisplayName() : null), - new ItemEntry<>(Bundle.Annotations_tagEntryDataLabel_tagUser(), (tag) -> tag.getUserName()), - new ItemEntry<>(Bundle.Annotations_tagEntryDataLabel_comment(), (tag) -> tag.getComment()) + new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_tagUser(), (tag) -> tag.getUserName()), + new ItemEntry<>(Bundle.AnnotationUtils_tagEntryDataLabel_comment(), (tag) -> tag.getComment()) ); private static final SectionConfig TAG_CONFIG - = new SectionConfig<>(Bundle.Annotations_tagEntry_title(), TAG_ENTRIES); + = new SectionConfig<>(Bundle.AnnotationUtils_tagEntry_title(), TAG_ENTRIES); // file set attributes and table configurations private static final List> FILESET_HIT_ENTRIES = Arrays.asList( - new ItemEntry<>(Bundle.Annotations_fileHitEntry_setName(), + new ItemEntry<>(Bundle.AnnotationUtils_fileHitEntry_setName(), (bba) -> tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)), - new ItemEntry<>(Bundle.Annotations_fileHitEntry_comment(), + new ItemEntry<>(Bundle.AnnotationUtils_fileHitEntry_comment(), (bba) -> tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT)) ); private static final SectionConfig INTERESTING_FILE_CONFIG - = new SectionConfig<>(Bundle.Annotations_fileHitEntry_interestingFileHitTitle(), FILESET_HIT_ENTRIES); + = new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_interestingFileHitTitle(), FILESET_HIT_ENTRIES); private static final SectionConfig HASHSET_CONFIG - = new SectionConfig<>(Bundle.Annotations_fileHitEntry_hashSetHitTitle(), FILESET_HIT_ENTRIES); + = new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_hashSetHitTitle(), FILESET_HIT_ENTRIES); private static final SectionConfig ARTIFACT_COMMENT_CONFIG - = new SectionConfig<>(Bundle.Annotations_fileHitEntry_artifactCommentTitle(), FILESET_HIT_ENTRIES); + = new SectionConfig<>(Bundle.AnnotationUtils_fileHitEntry_artifactCommentTitle(), FILESET_HIT_ENTRIES); // central repository attributes and table configuration private static final List> CR_COMMENTS_ENTRIES = Arrays.asList( - new ItemEntry<>(Bundle.Annotations_centralRepositoryEntryDataLabel_case(), + new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_case(), cai -> (cai.getCorrelationCase() != null) ? cai.getCorrelationCase().getDisplayName() : null), - new ItemEntry<>(Bundle.Annotations_centralRepositoryEntryDataLabel_comment(), cai -> cai.getComment()), - new ItemEntry<>(Bundle.Annotations_centralRepositoryEntryDataLabel_path(), cai -> cai.getFilePath()) + new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_comment(), cai -> cai.getComment()), + new ItemEntry<>(Bundle.AnnotationUtils_centralRepositoryEntryDataLabel_path(), cai -> cai.getFilePath()) ); private static final SectionConfig CR_COMMENTS_CONFIG - = new SectionConfig<>(Bundle.Annotations_centralRepositoryEntry_title(), CR_COMMENTS_ENTRIES); + = new SectionConfig<>(Bundle.AnnotationUtils_centralRepositoryEntry_title(), CR_COMMENTS_ENTRIES); /* * Private constructor for this utility class. */ - private Annotations() { + private AnnotationUtils() { } @@ -205,7 +205,7 @@ public class Annotations { contentRendered = contentRendered || filesetRendered; } - Element sourceFileSection = appendSection(parent, Bundle.Annotations_sourceFile_title()); + Element sourceFileSection = appendSection(parent, Bundle.AnnotationUtils_sourceFile_title()); sourceFileSection.attr("class", ContentViewerHtmlStyles.getSpacedSectionClassName()); Element sourceFileContainer = sourceFileSection.appendElement("div"); @@ -372,7 +372,7 @@ public class Annotations { return new ArrayList<>(); } - List> lookupKeys = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(artifact) + List> lookupKeys = CorrelationAttributeUtil.makeCorrAttrsForSearch(artifact) .stream() .map(cai -> Pair.of(cai.getCorrelationType(), cai.getCorrelationValue())) .collect(Collectors.toList()); @@ -464,7 +464,7 @@ public class Annotations { * * @return If there was actual content rendered for this set of entries. */ - private static boolean appendEntries(Element parent, Annotations.SectionConfig config, List items, + private static boolean appendEntries(Element parent, AnnotationUtils.SectionConfig config, List items, boolean isSubsection, boolean isFirstSection) { if (items == null || items.isEmpty()) { return false; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationsContentViewer.form similarity index 100% rename from Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.form rename to Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationsContentViewer.form diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationsContentViewer.java similarity index 95% rename from Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationsContentViewer.java index b61abfceee..d40f4924f0 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/AnnotationsContentViewer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.contentviewers; +package org.sleuthkit.autopsy.contentviewers.annotations; import java.awt.Component; import java.util.concurrent.ExecutionException; @@ -28,9 +28,6 @@ import org.openide.nodes.Node; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.contentviewers.application.Annotations; import org.sleuthkit.autopsy.coreutils.Logger; import org.jsoup.nodes.Document; import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles; @@ -161,7 +158,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data @Override protected String doInBackground() throws Exception { - Document doc = Annotations.buildDocument(node); + Document doc = AnnotationUtils.buildDocument(node); if(isCancelled()) { return null; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle.properties-MERGED new file mode 100755 index 0000000000..f2be329390 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle.properties-MERGED @@ -0,0 +1,21 @@ +AnnotationsContentViewer.onEmpty=No annotations were found for this particular item. +AnnotationsContentViewer.title=Annotations +AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. +AnnotationUtils.centralRepositoryEntry.title=Central Repository Comments +AnnotationUtils.centralRepositoryEntryDataLabel.case=Case: +AnnotationUtils.centralRepositoryEntryDataLabel.comment=Comment: +AnnotationUtils.centralRepositoryEntryDataLabel.path=Path: +AnnotationUtils.centralRepositoryEntryDataLabel.type=Type: +AnnotationUtils.fileHitEntry.artifactCommentTitle=Artifact Comment +AnnotationUtils.fileHitEntry.comment=Comment: +AnnotationUtils.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments +AnnotationUtils.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments +AnnotationUtils.fileHitEntry.setName=Set Name: +AnnotationUtils.onEmpty=No annotations were found for this particular item. +AnnotationUtils.sourceFile.title=Source File +AnnotationUtils.tagEntry.title=Tags +AnnotationUtils.tagEntryDataLabel.comment=Comment: +AnnotationUtils.tagEntryDataLabel.tag=Tag: +AnnotationUtils.tagEntryDataLabel.tagUser=Examiner: +AnnotationUtils.title=Annotations +AnnotationUtils.toolTip=Displays tags and comments associated with the selected content. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle_ja.properties new file mode 100644 index 0000000000..5c3ff1bfb2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/annotations/Bundle_ja.properties @@ -0,0 +1,22 @@ +#Thu Jul 01 11:56:41 UTC 2021 +AnnotationUtils.centralRepositoryEntry.title=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ea\u30dd\u30b8\u30c8\u30ea\u306e\u30b3\u30e1\u30f3\u30c8 +AnnotationUtils.centralRepositoryEntryDataLabel.case=\u30b1\u30fc\u30b9\: +AnnotationUtils.centralRepositoryEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8\: +AnnotationUtils.centralRepositoryEntryDataLabel.path=\u30d1\u30b9\: +AnnotationUtils.centralRepositoryEntryDataLabel.type=\u30bf\u30a4\u30d7\: +AnnotationUtils.fileHitEntry.artifactCommentTitle=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30b3\u30e1\u30f3\u30c8 +AnnotationUtils.fileHitEntry.comment=\u30b3\u30e1\u30f3\u30c8\: +AnnotationUtils.fileHitEntry.hashSetHitTitle=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30fb\u30d2\u30c3\u30c8\u30b3\u30e1\u30f3\u30c8 +AnnotationUtils.fileHitEntry.interestingFileHitTitle=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30d2\u30c3\u30c8\u30b3\u30e1\u30f3\u30c8 +AnnotationUtils.fileHitEntry.setName=\u30bb\u30c3\u30c8\u540d\: +AnnotationUtils.onEmpty=\u3053\u306e\u30a2\u30a4\u30c6\u30e0\u306e\u6ce8\u91c8\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +AnnotationUtils.sourceFile.title=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb +AnnotationUtils.tagEntry.title=\u30bf\u30b0 +AnnotationUtils.tagEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8\: +AnnotationUtils.tagEntryDataLabel.tag=\u30bf\u30b0\: +AnnotationUtils.tagEntryDataLabel.tagUser=\u5be9\u67fb\u5b98\uff1a +AnnotationUtils.title=\u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3 +AnnotationUtils.toolTip=\u9078\u629e\u3057\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +AnnotationsContentViewer.onEmpty=\u3053\u306e\u30a2\u30a4\u30c6\u30e0\u306e\u6ce8\u91c8\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +AnnotationsContentViewer.title=\u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3 +AnnotationsContentViewer.toolTip=\u9078\u629e\u3057\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle.properties-MERGED deleted file mode 100755 index 238576e0c7..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle.properties-MERGED +++ /dev/null @@ -1,18 +0,0 @@ -Annotations.centralRepositoryEntry.title=Central Repository Comments -Annotations.centralRepositoryEntryDataLabel.case=Case: -Annotations.centralRepositoryEntryDataLabel.comment=Comment: -Annotations.centralRepositoryEntryDataLabel.path=Path: -Annotations.centralRepositoryEntryDataLabel.type=Type: -Annotations.fileHitEntry.artifactCommentTitle=Artifact Comment -Annotations.fileHitEntry.comment=Comment: -Annotations.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments -Annotations.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments -Annotations.fileHitEntry.setName=Set Name: -Annotations.onEmpty=No annotations were found for this particular item. -Annotations.sourceFile.title=Source File -Annotations.tagEntry.title=Tags -Annotations.tagEntryDataLabel.comment=Comment: -Annotations.tagEntryDataLabel.tag=Tag: -Annotations.tagEntryDataLabel.tagUser=Examiner: -Annotations.title=Annotations -Annotations.toolTip=Displays tags and comments associated with the selected content. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle_ja.properties deleted file mode 100644 index ad13ec51b3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/application/Bundle_ja.properties +++ /dev/null @@ -1,19 +0,0 @@ -#Thu Jul 01 11:56:41 UTC 2021 -Annotations.centralRepositoryEntry.title=\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ea\u30dd\u30b8\u30c8\u30ea\u306e\u30b3\u30e1\u30f3\u30c8 -Annotations.centralRepositoryEntryDataLabel.case=\u30b1\u30fc\u30b9\: -Annotations.centralRepositoryEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8\: -Annotations.centralRepositoryEntryDataLabel.path=\u30d1\u30b9\: -Annotations.centralRepositoryEntryDataLabel.type=\u30bf\u30a4\u30d7\: -Annotations.fileHitEntry.artifactCommentTitle=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30b3\u30e1\u30f3\u30c8 -Annotations.fileHitEntry.comment=\u30b3\u30e1\u30f3\u30c8\: -Annotations.fileHitEntry.hashSetHitTitle=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u30fb\u30d2\u30c3\u30c8\u30b3\u30e1\u30f3\u30c8 -Annotations.fileHitEntry.interestingFileHitTitle=\u8208\u5473\u6df1\u3044\u30d5\u30a1\u30a4\u30eb\u30d2\u30c3\u30c8\u30b3\u30e1\u30f3\u30c8 -Annotations.fileHitEntry.setName=\u30bb\u30c3\u30c8\u540d\: -Annotations.onEmpty=\u3053\u306e\u30a2\u30a4\u30c6\u30e0\u306e\u6ce8\u91c8\u306f\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 -Annotations.sourceFile.title=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb -Annotations.tagEntry.title=\u30bf\u30b0 -Annotations.tagEntryDataLabel.comment=\u30b3\u30e1\u30f3\u30c8\: -Annotations.tagEntryDataLabel.tag=\u30bf\u30b0\: -Annotations.tagEntryDataLabel.tagUser=\u5be9\u67fb\u5b98\uff1a -Annotations.title=\u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3 -Annotations.toolTip=\u9078\u629e\u3057\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties index 341efff6d6..fb7c0f33cc 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties @@ -8,7 +8,7 @@ StringsContentPanel.selectAllMenuItem.text=Select All StringsContentPanel.currentPageLabel.text_1=1 StringsContentPanel.copyMenuItem.text=Copy StringsContentPanel.ofLabel.text_1=of -StringsContentPanel.totalPageLabel.text_1=100 +StringsContentPanel.totalPageLabel.text_1=1000 StringsContentPanel.languageLabel.toolTipText= StringsContentPanel.languageLabel.text=Script: StringsContentPanel.languageCombo.toolTipText=Language to attempt when interpreting (extracting and decoding) strings from binary data diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties-MERGED index 024103570d..c859dc8f33 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/Bundle.properties-MERGED @@ -9,7 +9,7 @@ StringsContentPanel.selectAllMenuItem.text=Select All StringsContentPanel.currentPageLabel.text_1=1 StringsContentPanel.copyMenuItem.text=Copy StringsContentPanel.ofLabel.text_1=of -StringsContentPanel.totalPageLabel.text_1=100 +StringsContentPanel.totalPageLabel.text_1=1000 StringsContentPanel.languageLabel.toolTipText= StringsContentPanel.languageLabel.text=Script: StringsContentPanel.languageCombo.toolTipText=Language to attempt when interpreting (extracting and decoding) strings from binary data diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.form index 6253570c82..6c579799b6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.form @@ -103,15 +103,6 @@ - - - - - - - - - @@ -158,13 +149,13 @@ - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.java index 750f143e0e..e83dadadef 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsContentPanel.java @@ -95,12 +95,11 @@ public class StringsContentPanel extends javax.swing.JPanel { currentPage = 1; currentOffset = 0; this.dataSource = null; - currentPageLabel.setText(""); + currentPageLabel.setText("1"); totalPageLabel.setText(""); prevPageButton.setEnabled(false); nextPageButton.setEnabled(false); outputViewPane.setText(""); // reset the output view - setComponentsVisibility(false); // hides the components that not needed } /** @@ -167,9 +166,6 @@ public class StringsContentPanel extends javax.swing.JPanel { panelPageOfCount.add(jSepMed1); currentPageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.currentPageLabel.text_1")); // NOI18N - currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 25)); - currentPageLabel.setMinimumSize(new java.awt.Dimension(7, 25)); - currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 25)); panelPageOfCount.add(currentPageLabel); jSepMed2.setPreferredSize(new java.awt.Dimension(5, 0)); @@ -185,9 +181,9 @@ public class StringsContentPanel extends javax.swing.JPanel { panelPageOfCount.add(jSepMed3); totalPageLabel.setText(org.openide.util.NbBundle.getMessage(StringsContentPanel.class, "StringsContentPanel.totalPageLabel.text_1")); // NOI18N - totalPageLabel.setMaximumSize(new java.awt.Dimension(21, 25)); - totalPageLabel.setMinimumSize(new java.awt.Dimension(21, 25)); - totalPageLabel.setPreferredSize(new java.awt.Dimension(21, 25)); + totalPageLabel.setMaximumSize(new java.awt.Dimension(25, 25)); + totalPageLabel.setMinimumSize(new java.awt.Dimension(25, 25)); + totalPageLabel.setPreferredSize(new java.awt.Dimension(25, 25)); panelPageOfCount.add(totalPageLabel); jSepMed4.setPreferredSize(new java.awt.Dimension(5, 0)); @@ -409,24 +405,6 @@ public class StringsContentPanel extends javax.swing.JPanel { worker.execute(); } - /** - * To set the visibility of specific components in this class. - * - * @param isVisible whether to show or hide the specific components - */ - private void setComponentsVisibility(boolean isVisible) { - currentPageLabel.setVisible(isVisible); - totalPageLabel.setVisible(isVisible); - ofLabel.setVisible(isVisible); - prevPageButton.setVisible(isVisible); - nextPageButton.setVisible(isVisible); - pageLabel.setVisible(isVisible); - pageLabel2.setVisible(isVisible); - goToPageTextField.setVisible(isVisible); - goToPageLabel.setVisible(isVisible); - languageCombo.setVisible(isVisible); - languageLabel.setVisible(isVisible); - } /** * Swingworker for getting the text from a content object. @@ -509,9 +487,7 @@ public class StringsContentPanel extends javax.swing.JPanel { int totalPage = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1; totalPageLabel.setText(Integer.toString(totalPage)); - currentPageLabel.setText("1"); outputViewPane.setText(text); // set the output view - setComponentsVisibility(true); // shows the components that not needed outputViewPane.moveCaretPosition(0); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); @@ -557,10 +533,7 @@ public class StringsContentPanel extends javax.swing.JPanel { prevPageButton.setEnabled(false); currentPage = 1; - totalPageLabel.setText("1"); - currentPageLabel.setText("1"); outputViewPane.setText(text); // set the output view - setComponentsVisibility(true); // shows the components that not needed outputViewPane.moveCaretPosition(0); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java index d7f853caab..426163ef80 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java @@ -24,6 +24,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; /** * A DataContentViewer that displays text with the TextViewers available. @@ -91,14 +92,10 @@ public class TextContentViewer implements DataContentViewer { if (node == null) { return false; } + // get the node's File, if it has one AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (file == null) { - return false; - } - - // disable the text content viewer for directories and empty files - if (file.isDir() || file.getSize() == 0) { + if (file != null && (file.isDir() || file.getSize() == 0)) { return false; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 0a43d47102..cb4bb9794c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2019 Basis Technology Corp. + * Copyright 2012-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,7 +39,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Score; @@ -102,7 +101,7 @@ public abstract class AbstractContentNode extends ContentNode * @param content Underlying Content instances */ AbstractContentNode(T content) { - this(content, Lookups.singleton(content)); + this(content, Lookups.fixed(content, new TskContentItem<>(content))); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AnalysisResultItem.java b/Core/src/org/sleuthkit/autopsy/datamodel/AnalysisResultItem.java new file mode 100755 index 0000000000..20ee3652c0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AnalysisResultItem.java @@ -0,0 +1,41 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021-2021 Basis Technology Corp. + * Contact: carrier sleuthkit 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 com.google.common.annotations.Beta; +import org.sleuthkit.datamodel.AnalysisResult; + +/** + * An Autopsy Data Model item with an underlying AnalysisResult Sleuth Kit Data + * Model object. + */ +public class AnalysisResultItem extends BlackboardArtifactItem { + + /** + * Constructs an Autopsy Data Model item with an underlying AnalysisResult + * Sleuth Kit Data Model object. + * + * @param analysisResult The AnalysisResult object. + */ + @Beta + AnalysisResultItem(AnalysisResult analysisResult) { + super(analysisResult); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactItem.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactItem.java new file mode 100755 index 0000000000..8d4cab5ea5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactItem.java @@ -0,0 +1,44 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021-2021 Basis Technology Corp. + * Contact: carrier sleuthkit 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 com.google.common.annotations.Beta; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * An abstract super class for an Autopsy Data Model item class with an + * underlying BlackboardArtifact Sleuth Kit Data Model object, i.e., a + * DataArtifact or an AnalysisResult. + * + * @param The concrete BlackboardArtifact sub class type. + */ +public abstract class BlackboardArtifactItem extends TskContentItem { + + /** + * Constructs an Autopsy Data Model item with an underlying + * BlackboardArtifact Sleuth Kit Data Model object. + * + * @param blackboardArtifact The BlackboardArtifact object. + */ + @Beta + BlackboardArtifactItem(T blackboardArtifact) { + super(blackboardArtifact); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index d7195ce23c..1fc1d9f48a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -87,13 +87,14 @@ import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.HostAddress; import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.Pool; +import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.Volume; import org.sleuthkit.datamodel.VolumeSystem; /** - * A BlackboardArtifactNode is an AbstractNode implementation that can be used - * to represent an artifact of any type. + * An AbstractNode implementation that can be used to represent an data artifact + * or analysis result of any type. */ public class BlackboardArtifactNode extends AbstractContentNode { @@ -227,37 +228,38 @@ public class BlackboardArtifactNode extends AbstractContentNode artifact.getSleuthkitCase().getContentById(objectID)); - if (content == null) { - return Lookups.fixed(artifact); + if (useAssociatedFile) { + content = getPathIdFile(artifact); } else { - return Lookups.fixed(artifact, content); + long srcObjectID = artifact.getObjectID(); + content = contentCache.get(srcObjectID, () -> artifact.getSleuthkitCase().getContentById(srcObjectID)); } } catch (ExecutionException ex) { - logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS - return Lookups.fixed(artifact); + logger.log(Level.SEVERE, MessageFormat.format("Error getting source/associated content (artifact object ID={0})", artifact.getId()), ex); //NON-NLS + } + + /* + * Make an Autopsy Data Model wrapper for the artifact. + * + * NOTE: The creation of an Autopsy Data Model independent of the + * NetBeans nodes is a work in progress. At the time this comment is + * being written, this object is only being used to indicate the item + * represented by this BlackboardArtifactNode. + */ + BlackboardArtifactItem artifactItem; + if (artifact instanceof AnalysisResult) { + artifactItem = new AnalysisResultItem((AnalysisResult) artifact); + } else { + artifactItem = new DataArtifactItem((DataArtifact) artifact); + } + + /* + * Create the Lookup. + * + * NOTE: For now, we are putting both the Autopsy Data Model item and + * the Sleuth Kit Data Model item in the Lookup so that code that is not + * aware of the new Autopsy Data Model will still function. + */ + if (content == null) { + return Lookups.fixed(artifact, artifactItem); + } else { + return Lookups.fixed(artifact, artifactItem, content); } } /** - * Creates a Lookup object for this node and populates it with both the - * artifact this node represents and its source content. + * Finds the source content in the Lookup created by createLookup() method. * - * @param artifact The artifact this node represents. - * @param lookupIsAssociatedFile True if the Content lookup should be made - * for the associated file instead of the - * parent file. + * @param artifact Artifact who's source Content we are trying to find. * - * @return The Lookup. + * @return Source Content of the input artifact, if one exists. Null + * otherwise. */ - private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) { - Content content = null; - if (lookupIsAssociatedFile) { - try { - content = getPathIdFile(artifact); - } catch (ExecutionException ex) { - logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS - content = null; + private Content getSourceContentFromLookup(BlackboardArtifact artifact) { + for (Content lookupContent : this.getLookup().lookupAll(Content.class)) { + /* + * NOTE: createLookup() saves the artifact and its source content + * (if one exists). However, createLookup() has to be static because + * it is being called by super(), therefore it can't store the + * source content in this.srcContent class variable. That's why we + * have to have the logic below, which reads the Lookup contents, + * and decides that the source content is the entry in Lookup that + * is NOT the input artifact. + */ + if ((lookupContent != null) && (lookupContent.getId() != artifact.getId())) { + return lookupContent; } - if (content == null) { - return Lookups.fixed(artifact); - } else { - return Lookups.fixed(artifact, content); - } - } else { - return createLookup(artifact); } - + return null; } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataArtifactItem.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataArtifactItem.java new file mode 100755 index 0000000000..c43d3a311c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataArtifactItem.java @@ -0,0 +1,41 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021-2021 Basis Technology Corp. + * Contact: carrier sleuthkit 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 com.google.common.annotations.Beta; +import org.sleuthkit.datamodel.DataArtifact; + +/** + * An Autopsy Data Model item with an underlying DataArtifact Sleuth Kit Data + * Model object. + */ +public class DataArtifactItem extends BlackboardArtifactItem { + + /** + * Constructs an Autopsy Data Model item with an underlying DataArtifact + * Sleuth Kit Data Model object. + * + * @param dataArtifact The DataArtifact object. + */ + @Beta + DataArtifactItem(DataArtifact dataArtifact) { + super(dataArtifact); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java index c6bc88129a..6edba5db30 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java @@ -99,6 +99,7 @@ public abstract class DisplayableItemNode extends AbstractNode { * operation on this artifact type and return some object as the result of * the operation. * + * @param The return type. * @param visitor The visitor, where the type parameter of the visitor is * the type of the object that will be returned as the result * of the visit operation. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index 5671ca6051..64b8aa604f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019-2020 Basis Technology Corp. + * Copyright 2019-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,19 +22,13 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.List; -import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; -import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Tag; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; /** @@ -46,7 +40,6 @@ class GetSCOTask implements Runnable { private final WeakReference> weakNodeRef; private final PropertyChangeListener listener; - private static final Logger logger = Logger.getLogger(GetSCOTask.class.getName()); GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { this.weakNodeRef = weakContentRef; @@ -59,7 +52,7 @@ class GetSCOTask implements Runnable { public void run() { AbstractContentNode contentNode = weakNodeRef.get(); - //Check for stale reference or if columns are disabled + //Check for stale reference or if columns are disabled if (contentNode == null || UserPreferences.getHideSCOColumns()) { return; } @@ -72,49 +65,18 @@ class GetSCOTask implements Runnable { //because the Comment column will reflect the presence of comments in the CR when the CR is enabled, but reflect tag comments regardless CorrelationAttributeInstance fileAttribute = contentNode.getCorrelationAttributeInstance(); scoData.setComment(contentNode.getCommentProperty(tags, fileAttribute)); - if (CentralRepository.isEnabled()) { Type type = null; String value = null; String description = Bundle.GetSCOTask_occurrences_defaultDescription(); - if (contentNode instanceof BlackboardArtifactNode) { - BlackboardArtifact bbArtifact = ((BlackboardArtifactNode) contentNode).getArtifact(); - //for specific artifact types we still want to display information for the file instance correlation attribute - if (bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID() - || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - try { - if (bbArtifact.getParent() instanceof AbstractFile) { - type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); - value = ((AbstractFile) bbArtifact.getParent()).getMd5Hash(); - } - } catch (TskCoreException | CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex); - } - } else { - List listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact); - 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.GetSCOTask_occurrences_multipleProperties(); - } else if (!listOfPossibleAttributes.isEmpty()) { - //there should only be one item in the list - type = listOfPossibleAttributes.get(0).getCorrelationType(); - value = listOfPossibleAttributes.get(0).getCorrelationValue(); - } - } - } else if (contentNode.getContent() instanceof AbstractFile) { - //use the file instance correlation attribute if the node is not a BlackboardArtifactNode - try { - type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); - value = ((AbstractFile) contentNode.getContent()).getMd5Hash(); - } catch (CentralRepoException ex) { - logger.log(Level.WARNING, "Unable to get correlation type to determine value for O column for file", ex); - } + List listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsForSearch(contentNode.getContent()); + 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.GetSCOTask_occurrences_multipleProperties(); + } else if (!listOfPossibleAttributes.isEmpty()) { + //there should only be one item in the list + type = listOfPossibleAttributes.get(0).getCorrelationType(); + value = listOfPossibleAttributes.get(0).getCorrelationValue(); } scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(type, value, description)); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TskContentItem.java b/Core/src/org/sleuthkit/autopsy/datamodel/TskContentItem.java new file mode 100755 index 0000000000..9113cf6b7c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/TskContentItem.java @@ -0,0 +1,58 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021-2021 Basis Technology Corp. + * Contact: carrier sleuthkit 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 com.google.common.annotations.Beta; +import org.sleuthkit.datamodel.Content; + +/** + * An Autopsy Data Model item with an underlying Sleuth Kit Data Model object + * that implements the Sleuth Kit Data Model's Content interface. + * + * @param The type of the underlying Sleuth Kit Data Model object. + */ +@Beta +public class TskContentItem { + + private final T content; + + /** + * Constructs an Autopsy Data Model item with an underlying Sleuth Kit Data + * Model object that implements the Sleuth Kit Data Model's Content + * interface. + * + * @param content The Sleuth Kit Data Model object. + * + */ + @Beta + TskContentItem(T content) { + this.content = content; + } + + /** + * Gets the underlying Sleuth Kit Data Model object. + * + * @return The Sleuth Kit Data Model object. + */ + @Beta + public T getTskContent() { + return content; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index bf7ee1caba..9c84b85bfe 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -1481,7 +1481,7 @@ final public class Accounts implements AutopsyVisitableItem { } } - final private class CreditCardNumberFactory extends ObservingChildren { + final private class CreditCardNumberFactory extends ObservingChildren { private final BinResult bin; @@ -1502,10 +1502,10 @@ final public class Accounts implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { String query - = "SELECT blackboard_artifacts.artifact_id " //NON-NLS + = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS + " FROM blackboard_artifacts " //NON-NLS + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS @@ -1517,7 +1517,7 @@ final public class Accounts implements AutopsyVisitableItem { try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query); ResultSet rs = results.getResultSet();) { while (rs.next()) { - list.add(rs.getLong("artifact_id")); //NON-NLS + list.add(skCase.getBlackboard().getDataArtifactById(rs.getLong("artifact_obj_id"))); //NON-NLS } } catch (TskCoreException | SQLException ex) { LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS @@ -1527,18 +1527,8 @@ final public class Accounts implements AutopsyVisitableItem { } @Override - protected Node[] createNodesForKey(Long artifactID) { - if (skCase == null) { - return new Node[0]; - } - - try { - DataArtifact art = skCase.getBlackboard().getDataArtifactById(artifactID); - return new Node[]{new AccountArtifactNode(art)}; - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS - return new Node[0]; - } + protected Node[] createNodesForKey(DataArtifact artifact) { + return new Node[]{new AccountArtifactNode(artifact)}; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java index de88e41e04..58bf8b26d2 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java @@ -127,6 +127,12 @@ public final class IconsUtil { imageFile = "gps-area.png"; //NON-NLS } else if (typeID == ARTIFACT_TYPE.TSK_YARA_HIT.getTypeID()) { imageFile = "yara_16.png"; //NON-NLS + } else if (typeID == ARTIFACT_TYPE.TSK_PREVIOUSLY_SEEN.getTypeID()) { + imageFile = "previously-seen.png"; //NON-NLS + } else if (typeID == ARTIFACT_TYPE.TSK_PREVIOUSLY_UNSEEN.getTypeID()) { + imageFile = "previously-unseen.png"; //NON-NLS + } else if (typeID == ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE.getTypeID()) { + imageFile = "red-circle-exclamation.png"; //NON-NLS } else { imageFile = "artifact-icon.png"; //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java index 66e265e128..07918f1f88 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2021 Basis Technology Corp. + * Copyright 2019-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,41 +25,43 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Provides information about how a datasource relates to a previous case. NOTE: - * This code is fragile and has certain expectations about how the central + * Provides information about how a data source relates to a previous case. + * NOTE: This code is fragile and has certain expectations about how the central * repository handles creating artifacts. So, if the central repository changes * ingest process, this code could break. This code expects that the central * repository ingest module: * - * a) Creates a TSK_INTERESTING_FILE_HIT artifact for a file whose hash is in - * the central repository as a notable file. + * a) Creates a TSK_PREVIOUSLY_NOTABLE artifact for a file whose hash is in the + * central repository as a notable file. * - * b) Creates a TSK_INTERESTING_ARTIFACT_HIT artifact for a matching id in the - * central repository. + * b) Creates a TSK_PREVIOUSLY_SEEN artifact for a matching id in the central + * repository. * - * c) The created artifact will have a TSK_COMMENT attribute attached where one - * of the sources for the attribute matches + * c) The created artifact will have a TSK_OTHER_CASES attribute attached where + * one of the sources for the attribute matches * CentralRepoIngestModuleFactory.getModuleName(). The module display name at * time of ingest will match CentralRepoIngestModuleFactory.getModuleName() as * well. * - * d) The content of that TSK_COMMENT attribute will be of the form "Previous - * Case: case1,case2...caseN" + * d) The content of that TSK_OTHER_CASES attribute will be of the form + * "case1,case2...caseN" */ public class PastCasesSummary { @@ -97,9 +99,13 @@ public class PastCasesSummary { } } + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_PREVIOUSLY_SEEN.getTypeID(), + ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE.getTypeID() + )); + private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim(); - private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT); - private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); + private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_OTHER_CASES); private static final Set CR_DEVICE_TYPE_IDS = new HashSet<>(Arrays.asList( ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(), @@ -109,7 +115,6 @@ public class PastCasesSummary { )); private static final String CASE_SEPARATOR = ","; - private static final String PREFIX_END = ":"; private final SleuthkitCaseProvider caseProvider; private final java.util.logging.Logger logger; @@ -161,9 +166,8 @@ public class PastCasesSummary { } /** - * Gets a list of cases from the TSK_COMMENT of an artifact. The cases - * string is expected to be of a form of "Previous Case: - * case1,case2...caseN". + * Gets a list of cases from the TSK_OTHER_CASES of an artifact. The cases + * string is expected to be of a form of "case1,case2...caseN". * * @param artifact The artifact. * @@ -189,14 +193,7 @@ public class PastCasesSummary { return Collections.emptyList(); } - String commentStr = commentAttr.getValueString(); - - int prefixCharIdx = commentStr.indexOf(PREFIX_END); - if (prefixCharIdx < 0 || prefixCharIdx >= commentStr.length() - 1) { - return Collections.emptyList(); - } - - String justCasesStr = commentStr.substring(prefixCharIdx + 1).trim(); + String justCasesStr = commentAttr.getValueString().trim(); return Stream.of(justCasesStr.split(CASE_SEPARATOR)) .map(String::trim) .collect(Collectors.toList()); @@ -231,30 +228,24 @@ public class PastCasesSummary { } /** - * Given an artifact with a TYPE_ASSOCIATED_ARTIFACT attribute, retrieves - * the related artifact. + * Given a TSK_PREVIOUSLY_SEEN or TSK_PREVIOUSLY_NOTABLE artifact, retrieves it's parent artifact. * - * @param artifact The artifact with the TYPE_ASSOCIATED_ARTIFACT attribute. + * @param artifact The input artifact. * * @return The artifact if found or null if not. * - * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws NoCurrentCaseException */ - private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException { - Long parentId = DataSourceInfoUtilities.getLongOrNull(artifact, TYPE_ASSOCIATED_ARTIFACT); - if (parentId == null) { - return null; - } + private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + BlackboardArtifact sourceArtifact = null; SleuthkitCase skCase = caseProvider.get(); - try { - return skCase.getArtifactByArtifactId(parentId); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, - String.format("There was an error fetching the parent artifact of a TSK_INTERESTING_ARTIFACT_HIT (parent id: %d)", parentId), - ex); - return null; + Content content = skCase.getContentById(artifact.getObjectID()); + if (content instanceof BlackboardArtifact) { + sourceArtifact = (BlackboardArtifact) content; } + return sourceArtifact; } /** @@ -264,9 +255,10 @@ public class PastCasesSummary { * * @return True if there is a device associated artifact. * - * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws NoCurrentCaseException */ - private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException { + private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { BlackboardArtifact parent = getParentArtifact(artifact); if (parent == null) { return false; @@ -284,6 +276,7 @@ public class PastCasesSummary { * * @throws SleuthkitCaseProviderException * @throws TskCoreException + * @throws NoCurrentCaseException */ public PastCasesResult getPastCasesData(DataSource dataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { @@ -296,26 +289,24 @@ public class PastCasesSummary { List deviceArtifactCases = new ArrayList<>(); List nonDeviceArtifactCases = new ArrayList<>(); + for (Integer typeId : ARTIFACT_UPDATE_TYPE_IDS) { + for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(typeId, dataSource.getId())) { + List cases = getCasesFromArtifact(artifact); + if (cases == null || cases.isEmpty()) { + continue; + } - for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(), dataSource.getId())) { - List cases = getCasesFromArtifact(artifact); - if (cases == null || cases.isEmpty()) { - continue; + if (hasDeviceAssociatedArtifact(artifact)) { + deviceArtifactCases.addAll(cases); + } else { + nonDeviceArtifactCases.addAll(cases); + } } - - if (hasDeviceAssociatedArtifact(artifact)) { - deviceArtifactCases.addAll(cases); - } else { - nonDeviceArtifactCases.addAll(cases); - } - } - - Stream filesCases = skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), dataSource.getId()).stream() - .flatMap((art) -> getCasesFromArtifact(art).stream()); - + } + return new PastCasesResult( getCaseCounts(deviceArtifactCases.stream()), - getCaseCounts(Stream.concat(filesCases, nonDeviceArtifactCases.stream())) + getCaseCounts(nonDeviceArtifactCases.stream()) ); } } diff --git a/Core/src/org/sleuthkit/autopsy/images/previously-seen.png b/Core/src/org/sleuthkit/autopsy/images/previously-seen.png new file mode 100644 index 0000000000..1bd707232e Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/previously-seen.png differ diff --git a/Core/src/org/sleuthkit/autopsy/images/previously-unseen.png b/Core/src/org/sleuthkit/autopsy/images/previously-unseen.png new file mode 100644 index 0000000000..02fc8b5008 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/previously-unseen.png differ diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataArtifactIngestPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/DataArtifactIngestPipeline.java index 824d7d7fe9..a25485a352 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataArtifactIngestPipeline.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataArtifactIngestPipeline.java @@ -44,10 +44,10 @@ final class DataArtifactIngestPipeline extends IngestTaskPipeline> acceptModuleTemplate(IngestModuleTemplate template) { Optional> module = Optional.empty(); - if (template.isDataArtifactIngestModuleTemplate()) { - DataArtifactIngestModule ingestModule = template.createDataArtifactIngestModule(); - module = Optional.of(new DataArtifactIngestPipelineModule(ingestModule, template.getModuleName())); - } +// if (template.isDataArtifactIngestModuleTemplate()) { +// DataArtifactIngestModule ingestModule = template.createDataArtifactIngestModule(); +// module = Optional.of(new DataArtifactIngestPipelineModule(ingestModule, template.getModuleName())); +// } return module; } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobPipeline.java index c68dde5911..bdaaa9f3a9 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobPipeline.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobPipeline.java @@ -368,9 +368,9 @@ final class IngestJobPipeline { if (template.isFileIngestModuleTemplate()) { addModuleTemplateToSortingMap(javaFileModuleTemplates, jythonFileModuleTemplates, template); } - if (template.isDataArtifactIngestModuleTemplate()) { - addModuleTemplateToSortingMap(javaArtifactModuleTemplates, jythonArtifactModuleTemplates, template); - } +// if (template.isDataArtifactIngestModuleTemplate()) { +// addModuleTemplateToSortingMap(javaArtifactModuleTemplates, jythonArtifactModuleTemplates, template); +// } } /** @@ -616,13 +616,13 @@ final class IngestJobPipeline { type = IngestModuleType.MULTIPLE; } } - if (moduleTemplate.isDataArtifactIngestModuleTemplate()) { - if (type == null) { - type = IngestModuleType.DATA_ARTIFACT; - } else { - type = IngestModuleType.MULTIPLE; - } - } +// if (moduleTemplate.isDataArtifactIngestModuleTemplate()) { +// if (type == null) { +// type = IngestModuleType.DATA_ARTIFACT; +// } else { +// type = IngestModuleType.MULTIPLE; +// } +// } return type; } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index 7de83ded4c..8f2db29849 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -318,7 +318,7 @@ public final class IngestJobSettings { // Add modules that are going to be used for this ingest depending on type. for (IngestModuleFactory moduleFactory : allModuleFactories) { - if (moduleFactory.isDataArtifactIngestModuleFactory() || ingestType.equals(IngestType.ALL_MODULES)) { + if (/*moduleFactory.isDataArtifactIngestModuleFactory() ||*/ ingestType.equals(IngestType.ALL_MODULES)) { moduleFactories.add(moduleFactory); } else if (this.ingestType.equals(IngestType.DATA_SOURCE_ONLY) && moduleFactory.isDataSourceIngestModuleFactory()) { moduleFactories.add(moduleFactory); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java index e473086d18..15fb96eded 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactory.java @@ -228,7 +228,7 @@ public interface IngestModuleFactory { * * @return A file ingest module instance. */ - default FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) { + default FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { throw new UnsupportedOperationException(); } @@ -238,9 +238,9 @@ public interface IngestModuleFactory { * * @return True or false. */ - default boolean isDataArtifactIngestModuleFactory() { - return false; - } +// default boolean isDataArtifactIngestModuleFactory() { +// return false; +// } /** * Creates a data artifact ingest module instance. @@ -267,8 +267,8 @@ public interface IngestModuleFactory { * * @return A file ingest module instance. */ - default DataArtifactIngestModule createDataArtifactIngestModule(IngestModuleIngestJobSettings settings) { - throw new UnsupportedOperationException(); - } +// default DataArtifactIngestModule createDataArtifactIngestModule(IngestModuleIngestJobSettings settings) { +// throw new UnsupportedOperationException(); +// } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java index 26285f6439..0bb947c1cd 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleTemplate.java @@ -85,13 +85,13 @@ public final class IngestModuleTemplate { return moduleFactory.createFileIngestModule(settings); } - public boolean isDataArtifactIngestModuleTemplate() { - return moduleFactory.isDataArtifactIngestModuleFactory(); - } +// public boolean isDataArtifactIngestModuleTemplate() { +// return moduleFactory.isDataArtifactIngestModuleFactory(); +// } - public DataArtifactIngestModule createDataArtifactIngestModule() { - return moduleFactory.createDataArtifactIngestModule(settings); - } +// public DataArtifactIngestModule createDataArtifactIngestModule() { +// return moduleFactory.createDataArtifactIngestModule(settings); +// } public void setEnabled(boolean enabled) { this.enabled = enabled; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/CustomFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/CustomFileTypesManager.java index 7175b3f023..57ce4c51b3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/CustomFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/CustomFileTypesManager.java @@ -336,6 +336,14 @@ final class CustomFileTypesManager { signatureList.add(new Signature(byteArray, 8L)); fileType = new FileType("application/x.android-hdb", signatureList); autopsyDefinedFileTypes.add(fileType); + + /** + * Add custom type for fixed-size VHDs. + */ + signatureList.clear(); + signatureList.add(new Signature("conectix", 511L, false)); //NON-NLS + fileType = new FileType("application/x-vhd", signatureList); //NON-NLS + autopsyDefinedFileTypes.add(fileType); } catch (IllegalArgumentException ex) { /* diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java index aa084d66f3..31323800b0 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java @@ -394,6 +394,15 @@ public class HTMLReport implements TableReportModule { case TSK_YARA_HIT: in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/yara_16.png"); //NON-NLS break; + case TSK_PREVIOUSLY_SEEN: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/previously-seen.png"); //NON-NLS + break; + case TSK_PREVIOUSLY_UNSEEN: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/previously-unseen.png"); //NON-NLS + break; + case TSK_PREVIOUSLY_NOTABLE: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/red-circle-exclamation.png"); //NON-NLS + break; default: logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java index c2b7ae5259..a6edfb4415 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Manifest.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,30 +30,44 @@ import javax.annotation.concurrent.Immutable; public final class Manifest implements Serializable { private static final long serialVersionUID = 1L; - private final Path filePath; + + // NOTE: Path is not Serializable. That's why we have to have a String as well, + // for scenarios when we send Manifest via ActiveMQ to other nodes. + private transient Path filePath; + private final String filePathString; + private transient Path dataSourcePath; + private final String dataSourcePathString; + private final Date dateFileCreated; private final String caseName; private final String deviceId; - private final Path dataSourcePath; private final String dataSourceFileName; private final Map manifestProperties; public Manifest(Path manifestFilePath, Date dateFileCreated, String caseName, String deviceId, Path dataSourcePath, Map manifestProperties) { - this.filePath = Paths.get(manifestFilePath.toString()); + this.filePathString = manifestFilePath.toString(); + this.filePath = Paths.get(filePathString); + this.dateFileCreated = new Date(dateFileCreated.getTime()); this.caseName = caseName; this.deviceId = deviceId; if (null != dataSourcePath) { - this.dataSourcePath = Paths.get(dataSourcePath.toString()); + this.dataSourcePathString = dataSourcePath.toString(); + this.dataSourcePath = Paths.get(dataSourcePathString); dataSourceFileName = dataSourcePath.getFileName().toString(); } else { + this.dataSourcePathString = ""; this.dataSourcePath = Paths.get(""); dataSourceFileName = ""; } this.manifestProperties = new HashMap<>(manifestProperties); - } + } public Path getFilePath() { + // after potential deserialization Path will be null because it is transient + if (filePath == null) { + this.filePath = Paths.get(filePathString); + } return this.filePath; } @@ -70,6 +84,10 @@ public final class Manifest implements Serializable { } public Path getDataSourcePath() { + // after potential deserialization Path will be null because it is transient + if (dataSourcePath == null) { + this.dataSourcePath = Paths.get(dataSourcePathString); + } return dataSourcePath; } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ShellBagParser.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ShellBagParser.java index 9d36233407..85c13c6a1d 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ShellBagParser.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ShellBagParser.java @@ -22,10 +22,12 @@ package org.sleuthkit.autopsy.recentactivity; import java.io.BufferedReader; -import java.io.FileReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -63,7 +65,7 @@ class ShellBagParser { ShellBagParser sbparser = new ShellBagParser(); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(regfile), StandardCharsets.UTF_8))) { String line = reader.readLine(); while (line != null) { line = line.trim(); diff --git a/thirdparty/rr-full/plugins/shellbags.pl b/thirdparty/rr-full/plugins/shellbags.pl index f4400cb4f0..b0e71ec299 100644 --- a/thirdparty/rr-full/plugins/shellbags.pl +++ b/thirdparty/rr-full/plugins/shellbags.pl @@ -779,7 +779,7 @@ sub parseFolderEntry { $tag = 0; } else { - $str .= $s; + $str .= $s; $cnt++; } } @@ -799,7 +799,7 @@ sub parseFolderEntry { $tag = 0; } else { - $str .= $s; + $str .= $s; $cnt++; } } @@ -858,13 +858,12 @@ sub parseFolderEntry { my $str = substr($data,$ofs,length($data) - 30); my $longname = (split(/\00\00/,$str,2))[0]; - $longname =~ s/\00//g; if ($longname ne "") { - $item{name} = $longname; + $item{name} = Utf16ToUtf8($longname); } else { - $item{name} = $shortname; + $item{name} = UTF16ToUtf8($shortname); } } return %item; @@ -957,7 +956,7 @@ sub parseFolderEntry2 { $item{name} = (split(/\00\00/,$str,2))[0]; $item{name} =~ s/\13\20/\2D\00/; - $item{name} =~ s/\00//g; + $item{name} = Utf16ToUtf8($item{name}); return %item; } @@ -1024,7 +1023,7 @@ sub shellItem0x52 { $tag = 0; } else { - $item{name} .= $d; + $item{name} .= $d; $cnt += 2; } } @@ -1119,4 +1118,15 @@ sub getNum48 { } } +#--------------------------------------------------------------------- +# Utf16ToUtf8() +#--------------------------------------------------------------------- +sub Utf16ToUtf8 { + my $str = $_[0]; + Encode::from_to($str,'UTF-16LE','utf8'); + $str = Encode::decode_utf8($str); + return $str; +} + + 1; \ No newline at end of file diff --git a/thirdparty/rr-full/plugins/shellbags_test.pl b/thirdparty/rr-full/plugins/shellbags_test.pl index 7ff3a5a4d5..3b068ea3ac 100644 --- a/thirdparty/rr-full/plugins/shellbags_test.pl +++ b/thirdparty/rr-full/plugins/shellbags_test.pl @@ -100,7 +100,7 @@ sub traverse { my $type = unpack("C",substr($values{$v},2,1)); my $size = unpack("v",substr($values{$v},0,2)); # probe($values{$v}); - + # Need to first check to see if the parent of the item was a zip folder # and if the 'zipsubfolder' value is set to 1 if (exists ${$parent}{zipsubfolder} && ${$parent}{zipsubfolder} == 1) { @@ -411,12 +411,13 @@ sub parseFolderItem { $longname =~ s/\x00//g; if ($longname ne "") { - $item{name} = $longname; + $item{name} = Utf16ToUtf8($longname); } else { - $item{name} = $shortname; + $item{name} = Utf16ToUtf8($shortname); } return %item; } + 1; diff --git a/thirdparty/rr-full/plugins/shellbags_xp.pl b/thirdparty/rr-full/plugins/shellbags_xp.pl index ce90cc3e7f..25082ea89b 100644 --- a/thirdparty/rr-full/plugins/shellbags_xp.pl +++ b/thirdparty/rr-full/plugins/shellbags_xp.pl @@ -776,13 +776,13 @@ sub parseFolderEntry { $str = substr($data,$ofs,length($data) - 30); my $longname = (split(/\x00\x00/,$str,2))[0]; - $longname =~ s/\x00//g; - + $longname = $longname.chr 0x00; + if ($longname ne "") { - $item{name} = $longname; + $item{name} = Utf16ToUtf8($longname); } else { - $item{name} = $shortname; + $item{name} = Utf16ToUtf8($shortname); } return %item; } @@ -871,7 +871,7 @@ sub parseFolderEntry2 { $item{name} = (split(/\x00\x00/,$str,2))[0]; $item{name} =~ s/\x13\x20/\x2D\x00/; - $item{name} =~ s/\x00//g; + $item{name} = Utf16ToUtf8($item{name}); return %item; } @@ -931,4 +931,14 @@ sub printData { return @display; } +#--------------------------------------------------------------------- +# Utf16ToUtf8() +#--------------------------------------------------------------------- +sub Utf16ToUtf8 { + my $str = $_[0]; + Encode::from_to($str,'UTF-16LE','utf8'); + my $str2 = Encode::decode_utf8($str); + return $str; +} + 1; diff --git a/thirdparty/rr-full/shellitems.pl b/thirdparty/rr-full/shellitems.pl index 5ec51cd6a2..93b71e7c20 100644 --- a/thirdparty/rr-full/shellitems.pl +++ b/thirdparty/rr-full/shellitems.pl @@ -27,6 +27,7 @@ # Author: H. Carvey, keydet89@yahoo.com #----------------------------------------------------------- use Time::Local; +use Encode::Unicode; my %guids = ("{bb64f8a7-bee7-4e1a-ab8d-7d8273f7fdb6}" => "Action Center", "{7a979262-40ce-46ff-aeee-7884ac3b6136}" => "Add Hardware", @@ -634,10 +635,10 @@ sub parseFolderEntry { $longname =~ s/\x00//g; if ($longname ne "") { - $item{name} = $longname; + $item{name} = Utf16ToUtf8($longname); } else { - $item{name} = $shortname; + $item{name} = Utf16ToUtf8($shortname); } return %item; } @@ -716,7 +717,7 @@ sub parseFolderEntry2 { $item{name} = (split(/\x00\x00/,$str,2))[0]; $item{name} =~ s/\x13\x20/\x2D\x00/; - $item{name} =~ s/\x00//g; + $item{name} = Utf16ToUtf8($item{name}); return %item; } @@ -837,4 +838,14 @@ sub getNum48 { } } +#--------------------------------------------------------------------- +# Utf16ToUtf8() +#--------------------------------------------------------------------- +sub Utf16ToUtf8 { + my $str = $_[0]; + Encode::from_to($str,'UTF-16LE','utf8'); + $str = Encode::decode_utf8($str); + return $str; +} + 1;