Merge branch 'develop' of github.com:sleuthkit/autopsy into 7413-unixScripts
@ -71,7 +71,7 @@ public class AddBlackboardArtifactTagAction extends AddTagAction {
|
||||
"AddBlackboardArtifactTagAction.singularTagResult");
|
||||
String pluralTagResult = NbBundle.getMessage(this.getClass(),
|
||||
"AddBlackboardArtifactTagAction.pluralTagResult");
|
||||
return Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).size() > 1 ? pluralTagResult : singularTagResult;
|
||||
return Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class).size() > 1 ? pluralTagResult : singularTagResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,12 +21,12 @@ package org.sleuthkit.autopsy.actions;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javax.swing.AbstractAction;
|
||||
@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem;
|
||||
import org.sleuthkit.autopsy.tags.TagUtils;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
@ -158,7 +159,11 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
TagMenu() {
|
||||
this(new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)));
|
||||
this(Utilities.actionsGlobalContext()
|
||||
.lookupAll(BlackboardArtifactItem.class)
|
||||
.stream()
|
||||
.map((bai) -> (BlackboardArtifact) bai.getTskContent())
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
TagMenu(Collection<BlackboardArtifact> selectedBlackboardArtifactsList) {
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.actions;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* An action that navigates to an artifact.
|
||||
*/
|
||||
public class ViewArtifactAction extends AbstractAction {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ViewArtifactAction.class.getName());
|
||||
private final BlackboardArtifact artifact;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param artifact The artifact to navigate to in the action.
|
||||
* @param displayName The display name of the menu item.
|
||||
*/
|
||||
public ViewArtifactAction(BlackboardArtifact artifact, String displayName) {
|
||||
super(displayName);
|
||||
this.artifact = artifact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
DirectoryTreeTopComponent.findInstance().viewArtifact(artifact);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
get();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected interrupt while navigating to artifact.", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Error navigating to artifact.", ex);
|
||||
}
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.actions;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* An action that navigates to an os account.
|
||||
*/
|
||||
public class ViewOsAccountAction extends AbstractAction {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ViewOsAccountAction.class.getName());
|
||||
|
||||
private final OsAccount osAccount;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param osAccount The os account to navigate to in the action.
|
||||
* @param displayName The display name of the menu item.
|
||||
*/
|
||||
public ViewOsAccountAction(OsAccount osAccount, String displayName) {
|
||||
super(displayName);
|
||||
this.osAccount = osAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
DirectoryTreeTopComponent.findInstance().viewOsAccount(osAccount);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
get();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected interrupt while navigating to OS Account.", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Error navigating to OS Account.", ex);
|
||||
}
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Copyright 2018-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -123,16 +123,6 @@ public class NodeData {
|
||||
comment = newComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a central repo node.
|
||||
*
|
||||
* @return true if this node was created from a central repo instance, false
|
||||
* otherwise
|
||||
*/
|
||||
public boolean isCentralRepoNode() {
|
||||
return (originalCorrelationInstance != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case name
|
||||
*
|
||||
|
@ -25,8 +25,8 @@ import java.nio.file.Files;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -46,16 +46,11 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountInstance;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -81,151 +76,26 @@ public final class OtherOccurrences {
|
||||
* @return A list of attributes that can be used for correlation
|
||||
*/
|
||||
public static Collection<CorrelationAttributeInstance> getCorrelationAttributeFromOsAccount(Node node, OsAccount osAccount) {
|
||||
Collection<CorrelationAttributeInstance> ret = new ArrayList<>();
|
||||
Optional<String> osAccountAddr = osAccount.getAddr();
|
||||
|
||||
if (osAccountAddr.isPresent()) {
|
||||
try {
|
||||
for (OsAccountInstance instance : osAccount.getOsAccountInstances()) {
|
||||
CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(instance.getOsAccount(), instance.getDataSource());
|
||||
if (correlationAttributeInstance != null) {
|
||||
ret.add(correlationAttributeInstance);
|
||||
for (OsAccountInstance instance : osAccount.getOsAccountInstances()) {
|
||||
List<CorrelationAttributeInstance> correlationAttributeInstances = CorrelationAttributeUtil.makeCorrAttrsForSearch(instance);
|
||||
if (!correlationAttributeInstances.isEmpty()) {
|
||||
return correlationAttributeInstances;
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for osAccount %s.", osAccountAddr.get()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what attributes can be used for correlation based on the node.
|
||||
* If EamDB is not enabled, get the default Files correlation.
|
||||
*
|
||||
* @param node The node to correlate.
|
||||
* @param file The file to correlate.
|
||||
*
|
||||
* @return A list of attributes that can be used for correlation
|
||||
*/
|
||||
public static Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNode(Node node, AbstractFile file) {
|
||||
Collection<CorrelationAttributeInstance> ret = new ArrayList<>();
|
||||
|
||||
// correlate on blackboard artifact attributes if they exist and supported
|
||||
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
|
||||
if (bbArtifact != null && CentralRepository.isEnabled()) {
|
||||
ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(bbArtifact));
|
||||
}
|
||||
|
||||
// we can correlate based on the MD5 if it is enabled
|
||||
if (file != null && CentralRepository.isEnabled() && file.getSize() > 0) {
|
||||
try {
|
||||
|
||||
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
||||
String md5 = file.getMd5Hash();
|
||||
if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) {
|
||||
for (CorrelationAttributeInstance.Type aType : artifactTypes) {
|
||||
if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
|
||||
CorrelationCase corCase = CentralRepository.getInstance().getCase(Case.getCurrentCase());
|
||||
try {
|
||||
ret.add(new CorrelationAttributeInstance(
|
||||
aType,
|
||||
md5,
|
||||
corCase,
|
||||
CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()),
|
||||
file.getParentPath() + file.getName(),
|
||||
"",
|
||||
file.getKnown(),
|
||||
file.getId()));
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CentralRepoException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
|
||||
}
|
||||
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled.
|
||||
} else if (file != null && file.getSize() > 0) {
|
||||
String md5 = file.getMd5Hash();
|
||||
if (md5 != null && !md5.isEmpty()) {
|
||||
try {
|
||||
final CorrelationAttributeInstance.Type fileAttributeType
|
||||
= CorrelationAttributeInstance.getDefaultCorrelationTypes()
|
||||
.stream()
|
||||
.filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
|
||||
.findAny()
|
||||
.get();
|
||||
//The Central Repository is not enabled
|
||||
ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, file.getId()));
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated BlackboardArtifact from a node, if it exists.
|
||||
*
|
||||
* @param node The node
|
||||
*
|
||||
* @return The associated BlackboardArtifact, or null
|
||||
*/
|
||||
public static BlackboardArtifact getBlackboardArtifactFromNode(Node node) {
|
||||
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class);
|
||||
BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class);
|
||||
|
||||
if (nodeBbArtifactTag != null) {
|
||||
return nodeBbArtifactTag.getArtifact();
|
||||
} else if (nodeBbArtifact != null) {
|
||||
return nodeBbArtifact;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated AbstractFile from a node, if it exists.
|
||||
*
|
||||
* @param node The node
|
||||
*
|
||||
* @return The associated AbstractFile, or null
|
||||
*/
|
||||
public static AbstractFile getAbstractFileFromNode(Node node) {
|
||||
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class);
|
||||
ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class);
|
||||
AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class);
|
||||
|
||||
if (nodeBbArtifactTag != null) {
|
||||
Content content = nodeBbArtifactTag.getContent();
|
||||
if (content instanceof AbstractFile) {
|
||||
return (AbstractFile) content;
|
||||
}
|
||||
} else if (nodeContentTag != null) {
|
||||
Content content = nodeContentTag.getContent();
|
||||
if (content instanceof AbstractFile) {
|
||||
return (AbstractFile) content;
|
||||
}
|
||||
} else if (nodeAbstractFile != null) {
|
||||
return nodeAbstractFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the central repo database (if enabled) and the case database to
|
||||
* find all artifact instances correlated to the given central repository
|
||||
* artifact. If the central repo is not enabled, this will only return files
|
||||
* from the current case with matching MD5 hashes.
|
||||
* artifact.
|
||||
*
|
||||
* @param file The current file.
|
||||
* @param deviceId The device ID for the current data source.
|
||||
@ -234,7 +104,7 @@ public final class OtherOccurrences {
|
||||
*
|
||||
* @return A collection of correlated artifact instances
|
||||
*/
|
||||
public static Map<UniquePathKey, NodeData> getCorrelatedInstances(AbstractFile file, String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) {
|
||||
public static Map<UniquePathKey, NodeData> getCorrelatedInstances(String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) {
|
||||
// @@@ Check exception
|
||||
try {
|
||||
final Case openCase = Case.getCurrentCaseThrows();
|
||||
@ -251,25 +121,21 @@ public final class OtherOccurrences {
|
||||
// - the case UUID is different
|
||||
// - the data source name is different
|
||||
// - the data source device ID is different
|
||||
// - the file path is different
|
||||
// - the object id for the underlying file is different
|
||||
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|
||||
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|
||||
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
&& (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
|
||||
continue;
|
||||
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))) {
|
||||
Long foundObjectId = artifactInstance.getFileObjectId();
|
||||
Long currentObjectId = corAttr.getFileObjectId();
|
||||
if (foundObjectId != null && currentObjectId != null && foundObjectId.equals(currentObjectId)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
NodeData newNode = new NodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
nodeDataMap.put(uniquePathKey, newNode);
|
||||
}
|
||||
}
|
||||
if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase, file);
|
||||
|
||||
for (AbstractFile caseDbFile : caseDbFiles) {
|
||||
addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile);
|
||||
}
|
||||
}
|
||||
return nodeDataMap;
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS
|
||||
@ -277,48 +143,12 @@ public final class OtherOccurrences {
|
||||
logger.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
// do nothing.
|
||||
// @@@ Review this behavior
|
||||
logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
return new HashMap<>(
|
||||
0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all other abstract files in the current case with the same MD5 as the
|
||||
* selected node.
|
||||
*
|
||||
* @param corAttr The CorrelationAttribute containing the MD5 to search for
|
||||
* @param openCase The current case
|
||||
* @param file The current file.
|
||||
*
|
||||
* @return List of matching AbstractFile objects
|
||||
*
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws TskCoreException
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
public static List<AbstractFile> getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException {
|
||||
List<AbstractFile> caseDbArtifactInstances = new ArrayList<>();
|
||||
if (file != null) {
|
||||
String md5 = corAttr.getCorrelationValue();
|
||||
SleuthkitCase tsk = openCase.getSleuthkitCase();
|
||||
List<AbstractFile> matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5}));
|
||||
|
||||
for (AbstractFile fileMatch : matches) {
|
||||
if (file.equals(fileMatch)) {
|
||||
continue; // If this is the file the user clicked on
|
||||
}
|
||||
caseDbArtifactInstances.add(fileMatch);
|
||||
}
|
||||
}
|
||||
return caseDbArtifactInstances;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the file to the nodeDataMap map if it does not already exist
|
||||
*
|
||||
@ -423,7 +253,7 @@ public final class OtherOccurrences {
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void writeOtherOccurrencesToFileAsCSV(File destFile, AbstractFile abstractFile, Collection<CorrelationAttributeInstance> correlationAttList, String dataSourceName, String deviceId) throws IOException {
|
||||
public static void writeOtherOccurrencesToFileAsCSV(File destFile, Collection<CorrelationAttributeInstance> correlationAttList, String dataSourceName, String deviceId) throws IOException {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) {
|
||||
//write headers
|
||||
StringBuilder headers = new StringBuilder("\"");
|
||||
@ -440,7 +270,7 @@ public final class OtherOccurrences {
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttList) {
|
||||
Map<UniquePathKey, NodeData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
// get correlation and reference set instances from DB
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(abstractFile, deviceId, dataSourceName, corAttr));
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(deviceId, dataSourceName, corAttr));
|
||||
for (NodeData nodeData : correlatedNodeDataMap.values()) {
|
||||
writer.write(nodeData.toCsvString());
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesPanel;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
|
@ -24,14 +24,16 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.JPanel;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.contentviewers.utils.ViewerPriority;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
|
||||
/**
|
||||
@ -89,20 +91,35 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
|
||||
// Is supported if one of the following is true:
|
||||
// - The central repo is enabled and the node is not null
|
||||
// - The central repo is disabled and the backing file has a valid MD5 hash
|
||||
// And the node has information which could be correlated on.
|
||||
//Ideally we would want to attempt to create correlation attributes for the node contents
|
||||
//and if none could be created determine that it was not supported.
|
||||
//However that winds up being more work than we really want to be performing in this method so we perform a quicker check.
|
||||
//The result of this is that the Other Occurrences viewer could be enabled but without any correlation attributes in some situations.
|
||||
// Is supported if:
|
||||
// The central repo is enabled and the node is not null
|
||||
if (CentralRepository.isEnabled() && node != null) {
|
||||
return OtherOccurrences.getAbstractFileFromNode(node) != null || OtherOccurrences.getBlackboardArtifactFromNode(node) != null || node.getLookup().lookup(OsAccount.class) != null;
|
||||
} else if (node != null) {
|
||||
AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node);
|
||||
return file != null
|
||||
&& file.getSize() > 0
|
||||
&& ((file.getMd5Hash() != null) && (!file.getMd5Hash().isEmpty()));
|
||||
// And the node has information which could be correlated on.
|
||||
if (node.getLookup().lookup(OsAccount.class) != null) {
|
||||
//the node has an associated OsAccount to correlate on
|
||||
return true;
|
||||
}
|
||||
if (node.getLookup().lookup(BlackboardArtifactItem.class) != null) {
|
||||
//it has a blackboard artifact which might have a correlation attribute
|
||||
return true;
|
||||
}
|
||||
if (node.getLookup().lookup(BlackboardArtifactTag.class) != null) {
|
||||
//Blackboard artifact tags may have their underlying artifact correlated on
|
||||
return true;
|
||||
}
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
//the AbstractFile lookup will handle the usecase for file tags as well
|
||||
if (file != null && !StringUtils.isBlank(file.getMd5Hash())) {
|
||||
//there is an abstractFile lookup and it has an MD5 so could be correlated on
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -29,7 +29,6 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -202,13 +201,9 @@ final class OccurrencePanel extends javax.swing.JPanel {
|
||||
}
|
||||
String caseDate = "";
|
||||
try {
|
||||
if (occurrence.isCentralRepoNode()) {
|
||||
if (CentralRepository.isEnabled()) {
|
||||
CorrelationCase partialCase = occurrence.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
caseDate = CentralRepository.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate();
|
||||
}
|
||||
} else {
|
||||
caseDate = Case.getCurrentCase().getCreatedDate();
|
||||
if (CentralRepository.isEnabled()) {
|
||||
CorrelationCase partialCase = occurrence.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
caseDate = CentralRepository.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate();
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error getting case created date for other occurrence content viewer", ex);
|
||||
|
@ -29,7 +29,6 @@ import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.UniquePathKey;
|
||||
@ -116,21 +115,11 @@ class OtherOccurrenceOneTypeWorker extends SwingWorker<OneTypeData, Void> {
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
try {
|
||||
dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
|
||||
}
|
||||
try {
|
||||
dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
totalCount++;
|
||||
}
|
||||
|
@ -28,16 +28,22 @@ import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
|
||||
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
|
||||
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.TskContentItem;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
|
||||
@ -62,64 +68,73 @@ class OtherOccurrencesNodeWorker extends SwingWorker<OtherOccurrencesData, Void>
|
||||
|
||||
@Override
|
||||
protected OtherOccurrencesData doInBackground() throws Exception {
|
||||
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node);
|
||||
if (osAccount != null) {
|
||||
file = node.getLookup().lookup(AbstractFile.class);
|
||||
}
|
||||
String deviceId = "";
|
||||
String dataSourceName = "";
|
||||
Map<String, CorrelationCase> caseNames = new HashMap<>();
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
OtherOccurrencesData data = null;
|
||||
try {
|
||||
if (file != null) {
|
||||
Content dataSource = file.getDataSource();
|
||||
deviceId = currentCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
|
||||
dataSourceName = dataSource.getName();
|
||||
if (CentralRepository.isEnabled()) {
|
||||
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
String deviceId = "";
|
||||
String dataSourceName = "";
|
||||
Map<String, CorrelationCase> caseNames = new HashMap<>();
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
//the file is currently being used for determining a correlation instance is not the selected instance
|
||||
// for the purposes of ignoring the currently selected item
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
try {
|
||||
if (file != null) {
|
||||
Content dataSource = file.getDataSource();
|
||||
deviceId = currentCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
|
||||
dataSourceName = dataSource.getName();
|
||||
}
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.WARNING, "Exception occurred while trying to get the data source, current case, and device id for an AbstractFile in the other occurrences viewer", ex);
|
||||
return data;
|
||||
}
|
||||
} catch (TskException ex) {
|
||||
// do nothing.
|
||||
// @@@ Review this behavior
|
||||
return null;
|
||||
}
|
||||
Collection<CorrelationAttributeInstance> correlationAttributes = new ArrayList<>();
|
||||
if (osAccount != null) {
|
||||
correlationAttributes = OtherOccurrences.getCorrelationAttributeFromOsAccount(node, osAccount);
|
||||
} else {
|
||||
correlationAttributes = OtherOccurrences.getCorrelationAttributesFromNode(node, file);
|
||||
}
|
||||
int totalCount = 0;
|
||||
Set<String> dataSources = new HashSet<>();
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
for (NodeData nodeData : OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr).values()) {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
Collection<CorrelationAttributeInstance> correlationAttributes = new ArrayList<>();
|
||||
if (osAccount != null) {
|
||||
correlationAttributes.addAll(OtherOccurrences.getCorrelationAttributeFromOsAccount(node, osAccount));
|
||||
} else {
|
||||
TskContentItem<?> contentItem = node.getLookup().lookup(TskContentItem.class);
|
||||
Content content = null;
|
||||
if (contentItem != null) {
|
||||
content = contentItem.getTskContent();
|
||||
} else { //fallback and check ContentTags
|
||||
ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class);
|
||||
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class);
|
||||
if (nodeBbArtifactTag != null) {
|
||||
content = nodeBbArtifactTag.getArtifact();
|
||||
} else if (nodeContentTag != null) {
|
||||
content = nodeContentTag.getContent();
|
||||
}
|
||||
}
|
||||
if (content != null) {
|
||||
if (content instanceof AbstractFile) {
|
||||
correlationAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AbstractFile) content));
|
||||
} else if (content instanceof AnalysisResult) {
|
||||
correlationAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) content));
|
||||
} else if (content instanceof DataArtifact) {
|
||||
correlationAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) content));
|
||||
}
|
||||
}
|
||||
}
|
||||
int totalCount = 0;
|
||||
Set<String> dataSources = new HashSet<>();
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
for (NodeData nodeData : OtherOccurrences.getCorrelatedInstances(deviceId, dataSourceName, corAttr).values()) {
|
||||
try {
|
||||
dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
|
||||
totalCount++;
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalCount++;
|
||||
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isCancelled()) {
|
||||
data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrences.getEarliestCaseDate());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCancelled()) {
|
||||
data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrences.getEarliestCaseDate());
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
|
||||
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
|
||||
}
|
||||
CSVWorker csvWorker = new CSVWorker(selectedFile, file, dataSourceName, deviceId, Collections.unmodifiableCollection(correlationAttributes));
|
||||
CSVWorker csvWorker = new CSVWorker(selectedFile, dataSourceName, deviceId, Collections.unmodifiableCollection(correlationAttributes));
|
||||
csvWorker.execute();
|
||||
}
|
||||
}
|
||||
@ -329,8 +329,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
casesTableModel.clearTable();
|
||||
|
||||
OtherOccurrenceOneTypeWorker.OneTypeData data = get();
|
||||
|
||||
OtherOccurrenceOneTypeWorker.OneTypeData data = get();
|
||||
correlationAttributes.addAll(data.getCorrelationAttributesToAdd());
|
||||
for (CorrelationCase corCase : data.getCaseNames().values()) {
|
||||
casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase));
|
||||
@ -426,7 +426,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) {
|
||||
worker = new SelectionWorker(correlationAttributes, deviceId, dataSourceName) {
|
||||
@Override
|
||||
public void done() {
|
||||
if (isCancelled()) {
|
||||
@ -447,12 +447,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
for (NodeData nodeData : correlatedNodeDataMap.values()) {
|
||||
for (int selectedRow : selectedCaseIndexes) {
|
||||
try {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null
|
||||
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) {
|
||||
dataSourcesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} else if (currentCaseName != null && (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(currentCaseName))) {
|
||||
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null
|
||||
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) {
|
||||
dataSourcesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
@ -491,7 +487,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
final int[] selectedDataSources = dataSourcesTable.getSelectedRows();
|
||||
filesTableModel.clearTable();
|
||||
|
||||
worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) {
|
||||
worker = new SelectionWorker(correlationAttributes, deviceId, dataSourceName) {
|
||||
@Override
|
||||
public void done() {
|
||||
if (isCancelled()) {
|
||||
@ -505,15 +501,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
for (int selectedDataSourceRow : selectedDataSources) {
|
||||
int rowModelIndex = dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow);
|
||||
try {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (dataSourcesTableModel.getCaseUUIDForRow(rowModelIndex).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())
|
||||
&& dataSourcesTableModel.getDeviceIdForRow(rowModelIndex).equals(nodeData.getDeviceID())) {
|
||||
filesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} else {
|
||||
if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
|
||||
filesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
if (dataSourcesTableModel.getCaseUUIDForRow(rowModelIndex).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())
|
||||
&& dataSourcesTableModel.getDeviceIdForRow(rowModelIndex).equals(nodeData.getDeviceID())) {
|
||||
filesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
|
||||
@ -618,7 +608,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
private class SelectionWorker extends SwingWorker<Map<UniquePathKey, NodeData>, Void> {
|
||||
|
||||
private final Collection<CorrelationAttributeInstance> coAtInstances;
|
||||
private final AbstractFile abstractFile;
|
||||
private final String deviceIdStr;
|
||||
private final String dataSourceNameStr;
|
||||
|
||||
@ -630,9 +619,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
* @param deviceIdStr
|
||||
* @param dataSourceNameStr
|
||||
*/
|
||||
SelectionWorker(Collection<CorrelationAttributeInstance> coAtInstances, AbstractFile abstractFile, String deviceIdStr, String dataSourceNameStr) {
|
||||
SelectionWorker(Collection<CorrelationAttributeInstance> coAtInstances, String deviceIdStr, String dataSourceNameStr) {
|
||||
this.coAtInstances = coAtInstances;
|
||||
this.abstractFile = abstractFile;
|
||||
this.dataSourceNameStr = dataSourceNameStr;
|
||||
this.deviceIdStr = deviceIdStr;
|
||||
}
|
||||
@ -641,7 +629,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
protected Map<UniquePathKey, NodeData> doInBackground() throws Exception {
|
||||
Map<UniquePathKey, NodeData> correlatedNodeDataMap = new HashMap<>();
|
||||
for (CorrelationAttributeInstance corAttr : coAtInstances) {
|
||||
correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(abstractFile, deviceIdStr, dataSourceNameStr, corAttr));
|
||||
correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(deviceIdStr, dataSourceNameStr, corAttr));
|
||||
|
||||
if (isCancelled()) {
|
||||
return new HashMap<>();
|
||||
@ -661,7 +649,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
private final String dataSourceName;
|
||||
private final String deviceId;
|
||||
private final File destFile;
|
||||
private final AbstractFile abstractFile;
|
||||
|
||||
/**
|
||||
* Construct a CSVWorker
|
||||
@ -672,9 +659,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
* @param deviceId Id of the selected device.
|
||||
* @param correlationAttList
|
||||
*/
|
||||
CSVWorker(File destFile, AbstractFile sourceFile, String dataSourceName, String deviceId, Collection<CorrelationAttributeInstance> correlationAttList) {
|
||||
CSVWorker(File destFile, String dataSourceName, String deviceId, Collection<CorrelationAttributeInstance> correlationAttList) {
|
||||
this.destFile = destFile;
|
||||
this.abstractFile = sourceFile;
|
||||
this.dataSourceName = dataSourceName;
|
||||
this.deviceId = deviceId;
|
||||
this.correlationAttList = correlationAttList;
|
||||
@ -682,7 +668,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
OtherOccurrences.writeOtherOccurrencesToFileAsCSV(this.destFile, this.abstractFile, this.correlationAttList, this.dataSourceName, this.deviceId);
|
||||
OtherOccurrences.writeOtherOccurrencesToFileAsCSV(this.destFile, this.correlationAttList, this.dataSourceName, this.deviceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -876,8 +862,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
int rowIndex = filesTable.getSelectedRow();
|
||||
List<NodeData> selectedFile = filesTableModel.getListOfNodesForFile(rowIndex);
|
||||
if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof NodeData) {
|
||||
NodeData instanceData = selectedFile.get(0);
|
||||
enableCentralRepoActions = instanceData.isCentralRepoNode();
|
||||
enableCentralRepoActions = true;
|
||||
}
|
||||
}
|
||||
showCaseDetailsMenuItem.setVisible(enableCentralRepoActions);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2020 Basis Technology Corp.
|
||||
* Copyright 2015-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -39,9 +40,11 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
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;
|
||||
|
||||
@ -78,13 +81,13 @@ public class CorrelationAttributeUtil {
|
||||
return Bundle.CorrelationAttributeUtil_emailaddresses_text();
|
||||
}
|
||||
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrsToSave(DataArtifact artifact) {
|
||||
public static List<CorrelationAttributeInstance> 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<>();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return CorrelationAttributeUtil.makeCorrAttrsForSearch(artifact);
|
||||
}
|
||||
@ -102,71 +105,80 @@ public class CorrelationAttributeUtil {
|
||||
* @return A list, possibly empty, of correlation attribute instances for
|
||||
* the content.
|
||||
*/
|
||||
public static List<CorrelationAttributeInstance> 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);
|
||||
}
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsToSave(AbstractFile file) {
|
||||
return makeCorrAttrsForSearch(file);
|
||||
}
|
||||
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsToSave(AnalysisResult file) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsToSave(OsAccountInstance osAccountInstance) {
|
||||
return makeCorrAttrsForSearch(osAccountInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* AnalysisResult 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 data to
|
||||
* search for != An artifact that should be the source of data in the CR, so
|
||||
* results may be too lenient.
|
||||
*
|
||||
* IMPORTANT: The correlation attribute instances are NOT added to the
|
||||
* central repository by this method.
|
||||
*
|
||||
* TODO (Jira-6088): The methods in this low-level, utility class should
|
||||
* throw exceptions instead of logging them. The reason for this is that the
|
||||
* clients of the utility class, not the utility class itself, should be in
|
||||
* charge of error handling policy, per the Autopsy Coding Standard. Note
|
||||
* that clients of several of these methods currently cannot determine
|
||||
* whether receiving a null return value is an error or not, plus null
|
||||
* checking is easy to forget, while catching exceptions is enforced.
|
||||
* JIRA-TODO (Jira-6088)
|
||||
*
|
||||
* @param Content A Content object.
|
||||
* @param analysisResult An AnalysisResult object.
|
||||
*
|
||||
* @return A list, possibly empty, of correlation attribute instances for
|
||||
* the content.
|
||||
* the AnalysisResult.
|
||||
*/
|
||||
public static List<CorrelationAttributeInstance> 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<CorrelationAttributeInstance> makeCorrAttrsForSearch(AnalysisResult analysisResult) {
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(AnalysisResult analysisResult) {
|
||||
List<CorrelationAttributeInstance> correlationAttrs = new ArrayList<>();
|
||||
try {
|
||||
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()) {
|
||||
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);
|
||||
if (sourceArtifact instanceof DataArtifact) {
|
||||
correlationAttrs.addAll((CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) sourceArtifact)));
|
||||
} else if (sourceArtifact instanceof AnalysisResult) {
|
||||
correlationAttrs.addAll((CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) sourceArtifact)));
|
||||
} else {
|
||||
String sourceName = sourceArtifact != null ? "SourceArtifact display name: " + sourceArtifact.getDisplayName() : "SourceArtifact was null";
|
||||
logger.log(Level.WARNING, "Source artifact found through TSK_ASSOCIATED_ARTIFACT attribute was not a DataArtifact or "
|
||||
+ "an Analysis Result. AssociateArtifactAttr Value: {0} {1}",
|
||||
new Object[]{assocArtifactAttr.getValueString(), sourceName});
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
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())) {
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(analysisResult, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID, analysisResult.getAttributes()));
|
||||
}
|
||||
}
|
||||
Content parent = analysisResult.getParent();
|
||||
if (parent instanceof AbstractFile) {
|
||||
correlationAttrs.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AbstractFile) parent));
|
||||
} else if (parent instanceof AnalysisResult) {
|
||||
correlationAttrs.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) parent));
|
||||
} else if (parent instanceof DataArtifact) {
|
||||
correlationAttrs.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) parent));
|
||||
} else if (parent instanceof OsAccount) {
|
||||
for (OsAccountInstance osAccountInst : ((OsAccount) parent).getOsAccountInstances()) {
|
||||
if (osAccountInst.getDataSource().equals(analysisResult.getDataSource())) {
|
||||
correlationAttrs.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(osAccountInst));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -177,7 +189,25 @@ public class CorrelationAttributeUtil {
|
||||
return correlationAttrs;
|
||||
}
|
||||
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(DataArtifact artifact) {
|
||||
/**
|
||||
* Makes zero to many correlation attribute instances from the attributes of
|
||||
* a DataArtifact 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 data to
|
||||
* search for != An artifact that should be the source of data in the CR, so
|
||||
* results may be too lenient.
|
||||
*
|
||||
* IMPORTANT: The correlation attribute instances are NOT added to the
|
||||
* central repository by this method.
|
||||
*
|
||||
* JIRA-TODO (Jira-6088)
|
||||
*
|
||||
* @param artifact A DataArtifact object.
|
||||
*
|
||||
* @return A list, possibly empty, of correlation attribute instances for
|
||||
* the DataArtifact.
|
||||
*/
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(DataArtifact artifact) {
|
||||
List<CorrelationAttributeInstance> correlationAttrs = new ArrayList<>();
|
||||
try {
|
||||
|
||||
@ -188,52 +218,50 @@ public class CorrelationAttributeUtil {
|
||||
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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI, CorrelationAttributeInstance.IMEI_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID,
|
||||
attributes, sourceContent, dataSource));
|
||||
|
||||
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
makeCorrAttrFromAcctArtifact(correlationAttrs, artifact);
|
||||
@ -245,14 +273,14 @@ public class CorrelationAttributeUtil {
|
||||
pathAttrString = setNameAttr.getValueString();
|
||||
}
|
||||
if (pathAttrString != null && !pathAttrString.isEmpty()) {
|
||||
makeCorrAttrFromArtifactAttr(correlationAttrs, artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, CorrelationAttributeInstance.INSTALLED_PROGS_TYPE_ID, attributes);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrFromArtifactAttr(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);
|
||||
correlationAttrs.addAll(makeCorrAttrsFromCommunicationArtifact(artifact, attributes));
|
||||
}
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error normalizing correlation attribute (%s)", artifact), ex); // NON-NLS
|
||||
@ -298,6 +326,7 @@ public class CorrelationAttributeUtil {
|
||||
*
|
||||
* @param corrAttrInstances Correlation attributes will be added to this.
|
||||
* @param artifact An artifact with a phone number attribute.
|
||||
* @param attributes List of attributes.
|
||||
*
|
||||
* @throws TskCoreException If there is an error
|
||||
* querying the case
|
||||
@ -309,8 +338,9 @@ public class CorrelationAttributeUtil {
|
||||
* in normalizing the
|
||||
* attribute.
|
||||
*/
|
||||
private static void makeCorrAttrsFromCommunicationArtifacts(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact,
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrsFromCommunicationArtifact(BlackboardArtifact artifact,
|
||||
List<BlackboardAttribute> attributes) throws TskCoreException, CentralRepoException, CorrelationAttributeNormalizationException {
|
||||
|
||||
/*
|
||||
* Extract the phone number from the artifact attribute.
|
||||
*/
|
||||
@ -325,6 +355,7 @@ public class CorrelationAttributeUtil {
|
||||
/*
|
||||
* Normalize the phone number.
|
||||
*/
|
||||
List<CorrelationAttributeInstance> corrAttrInstances = Collections.emptyList();
|
||||
if (value != null
|
||||
&& CorrelationAttributeNormalizer.isValidPhoneNumber(value)) {
|
||||
value = CorrelationAttributeNormalizer.normalizePhone(value);
|
||||
@ -333,6 +364,7 @@ public class CorrelationAttributeUtil {
|
||||
corrAttrInstances.add(corrAttr);
|
||||
}
|
||||
}
|
||||
return corrAttrInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -393,11 +425,12 @@ public class CorrelationAttributeUtil {
|
||||
*
|
||||
* @param corrAttrInstances A list of correlation attribute instances.
|
||||
* @param artifact An artifact.
|
||||
* @param artAttrType The type of the atrribute of the artifact that
|
||||
* is to be made into a correlatin attribute
|
||||
* @param artAttrType The type of the attribute of the artifact that
|
||||
* is to be made into a correlation attribute
|
||||
* instance.
|
||||
* @param typeId The type ID for the desired correlation
|
||||
* attribute instance.
|
||||
* @param attributes List of attributes.
|
||||
* @param sourceContent The source content object.
|
||||
* @param dataSource The data source content object.
|
||||
*
|
||||
@ -406,9 +439,9 @@ public class CorrelationAttributeUtil {
|
||||
* @throws TskCoreException If there is an error querying the case
|
||||
* database.
|
||||
*/
|
||||
private static void makeCorrAttrFromArtifactAttr(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId,
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrFromArtifactAttr(BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId,
|
||||
List<BlackboardAttribute> attributes, Content sourceContent, Content dataSource) throws CentralRepoException, TskCoreException {
|
||||
|
||||
List<CorrelationAttributeInstance> corrAttrInstances = new ArrayList<>();
|
||||
BlackboardAttribute attribute = getAttribute(attributes, new BlackboardAttribute.Type(artAttrType));
|
||||
if (attribute != null) {
|
||||
String value = attribute.getValueString();
|
||||
@ -419,6 +452,7 @@ public class CorrelationAttributeUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
return corrAttrInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -427,21 +461,22 @@ public class CorrelationAttributeUtil {
|
||||
*
|
||||
* @param corrAttrInstances A list of correlation attribute instances.
|
||||
* @param artifact An artifact.
|
||||
* @param artAttrType The type of the atrribute of the artifact that
|
||||
* is to be made into a correlatin attribute
|
||||
* @param artAttrType The type of the attribute of the artifact that
|
||||
* is to be made into a correlation attribute
|
||||
* instance.
|
||||
* @param typeId The type ID for the desired correlation
|
||||
* attribute instance.
|
||||
* @param attributes List of attributes.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error querying the central
|
||||
* repository.
|
||||
* @throws TskCoreException If there is an error querying the case
|
||||
* database.
|
||||
*/
|
||||
private static void makeCorrAttrFromArtifactAttr(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId,
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrFromArtifactAttr(BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId,
|
||||
List<BlackboardAttribute> attributes) throws CentralRepoException, TskCoreException {
|
||||
|
||||
makeCorrAttrFromArtifactAttr(corrAttrInstances, artifact, artAttrType, typeId, attributes, null, null);
|
||||
return makeCorrAttrFromArtifactAttr(artifact, artAttrType, typeId, attributes, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -550,51 +585,6 @@ public class CorrelationAttributeUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a correlation attribute instance of a given type from an OS
|
||||
* account. Checks address if it is null, or one of the ones always present
|
||||
* on a windows system and thus not unique.
|
||||
*
|
||||
* @param osAccoun The OS account.
|
||||
* @param dataSource The data source content object.
|
||||
*
|
||||
* @return The correlation attribute instance or null, if an error occurred.
|
||||
*/
|
||||
public static CorrelationAttributeInstance makeCorrAttr(OsAccount osAccount, Content dataSource) {
|
||||
|
||||
Optional<String> accountAddr = osAccount.getAddr();
|
||||
// Check address if it is null or one of the ones below we want to ignore it since they will always be one a windows system
|
||||
// and they are not unique
|
||||
if (!accountAddr.isPresent() || accountAddr.get().equals("S-1-5-18") || accountAddr.get().equals("S-1-5-19") || accountAddr.get().equals("S-1-5-20")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
||||
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
CorrelationAttributeInstance correlationAttributeInstance = new CorrelationAttributeInstance(
|
||||
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID),
|
||||
accountAddr.get(),
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource),
|
||||
"",
|
||||
"",
|
||||
TskData.FileKnown.KNOWN,
|
||||
osAccount.getId());
|
||||
|
||||
return correlationAttributeInstance;
|
||||
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", accountAddr.get()), ex); //NON-NLS
|
||||
return null;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return null;
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.SEVERE, "Exception with Correlation Attribute Normalization.", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ 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.
|
||||
/**
|
||||
@ -694,9 +684,10 @@ public class CorrelationAttributeUtil {
|
||||
*
|
||||
* @param file The file.
|
||||
*
|
||||
* @return The correlation attribute instance or null, if an error occurred.
|
||||
* @return The correlation attribute instance in a list, or an empty list if
|
||||
* an error occurred.
|
||||
*/
|
||||
private static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(AbstractFile file) {
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(AbstractFile file) {
|
||||
List<CorrelationAttributeInstance> fileTypeList = new ArrayList<>(); // will be an empty or single element list as was decided in 7852
|
||||
if (!isSupportedAbstractFileType(file)) {
|
||||
return fileTypeList;
|
||||
@ -765,6 +756,48 @@ public class CorrelationAttributeUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsForSearch(OsAccountInstance osAccountInst) {
|
||||
List<CorrelationAttributeInstance> correlationAttrs = new ArrayList<>();
|
||||
OsAccount account = null;
|
||||
DataSource dataSource = null;
|
||||
if (osAccountInst != null) {
|
||||
try {
|
||||
account = osAccountInst.getOsAccount();
|
||||
dataSource = osAccountInst.getDataSource();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting information from OsAccountInstance.", ex);
|
||||
}
|
||||
}
|
||||
if (account != null && dataSource != null) {
|
||||
Optional<String> accountAddr = account.getAddr();
|
||||
// Check address if it is null or one of the ones below we want to ignore it since they will always be one a windows system
|
||||
// and they are not unique
|
||||
if (accountAddr.isPresent() && !accountAddr.get().equals("S-1-5-18") && !accountAddr.get().equals("S-1-5-19") && !accountAddr.get().equals("S-1-5-20")) {
|
||||
try {
|
||||
|
||||
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
CorrelationAttributeInstance correlationAttributeInstance = new CorrelationAttributeInstance(
|
||||
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID),
|
||||
accountAddr.get(),
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource),
|
||||
"",
|
||||
"",
|
||||
TskData.FileKnown.KNOWN,
|
||||
account.getId());
|
||||
correlationAttrs.add(correlationAttributeInstance);
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", accountAddr.get()), ex); //NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
} catch (CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.SEVERE, "Exception with Correlation Attribute Normalization.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
return correlationAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent instantiation of this utility class.
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2020 Basis Technology Corp.
|
||||
* Copyright 2015-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -58,8 +58,8 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
*
|
||||
* @return the singleton instance of SqliteEamDb
|
||||
*
|
||||
* @throws CentralRepoException if one or more default correlation type(s) have an
|
||||
* invalid db table name.
|
||||
* @throws CentralRepoException if one or more default correlation type(s)
|
||||
* have an invalid db table name.
|
||||
*/
|
||||
public synchronized static SqliteCentralRepo getInstance() throws CentralRepoException {
|
||||
if (instance == null) {
|
||||
@ -71,9 +71,9 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws CentralRepoException if the AbstractSqlEamDb class has one or more
|
||||
* default correlation type(s) having an invalid db
|
||||
* table name.
|
||||
* @throws CentralRepoException if the AbstractSqlEamDb class has one or
|
||||
* more default correlation type(s) having an
|
||||
* invalid db table name.
|
||||
*/
|
||||
private SqliteCentralRepo() throws CentralRepoException {
|
||||
dbSettings = new SqliteCentralRepoSettings();
|
||||
@ -144,7 +144,7 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
CentralRepoDbUtil.closeConnection(conn);
|
||||
}
|
||||
|
||||
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, dbSettings);
|
||||
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, dbSettings);
|
||||
centralRepoSchemaFactory.insertDefaultDatabaseContent();
|
||||
} finally {
|
||||
releaseExclusiveLock();
|
||||
@ -229,8 +229,9 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
|
||||
@Override
|
||||
protected Connection getEphemeralConnection() {
|
||||
return this.dbSettings.getEphemeralConnection();
|
||||
}
|
||||
return this.dbSettings.getEphemeralConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new name/value pair in the db_info table.
|
||||
*
|
||||
@ -624,10 +625,10 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
releaseSharedLock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public Long getCountCasesWithOtherInstances(CorrelationAttributeInstance instance) throws CentralRepoException, CorrelationAttributeNormalizationException {
|
||||
try {
|
||||
try {
|
||||
acquireSharedLock();
|
||||
return super.getCountCasesWithOtherInstances(instance);
|
||||
} finally {
|
||||
@ -840,9 +841,9 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
super.processSelectClause(selectClause, instanceTableCallback);
|
||||
} finally {
|
||||
releaseSharedLock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(String sql, List<Object> params) throws CentralRepoException {
|
||||
try {
|
||||
@ -852,7 +853,7 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
releaseExclusiveLock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void executeQuery(String sql, List<Object> params, CentralRepositoryDbQueryCallback queryCallback) throws CentralRepoException {
|
||||
try {
|
||||
@ -862,7 +863,7 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
releaseSharedLock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a reference set with the given name/version is in the
|
||||
* central repo. Used to check for name collisions when creating reference
|
||||
@ -1224,8 +1225,8 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
|
||||
*
|
||||
* @return the lock, or null if locking is not supported
|
||||
*
|
||||
* @throws CentralRepoException if the coordination service is running but we fail
|
||||
* to get the lock
|
||||
* @throws CentralRepoException if the coordination service is running but
|
||||
* we fail to get the lock
|
||||
*/
|
||||
@Override
|
||||
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws CentralRepoException {
|
||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.eventlisteners;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
@ -64,12 +65,14 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNor
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
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.DataArtifact;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountInstance;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
@ -212,6 +215,28 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the known status of a blackboard artifact in the central repository.
|
||||
*
|
||||
* @param bbArtifact The blackboard artifact to set known status.
|
||||
* @param knownStatus The new known status.
|
||||
*/
|
||||
private static void setArtifactKnownStatus(CentralRepository dbManager, BlackboardArtifact bbArtifact, TskData.FileKnown knownStatus) {
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = new ArrayList<>();
|
||||
if (bbArtifact instanceof DataArtifact) {
|
||||
convertedArtifacts.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) bbArtifact));
|
||||
} else if (bbArtifact instanceof AnalysisResult) {
|
||||
convertedArtifacts.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) bbArtifact));
|
||||
}
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
try {
|
||||
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} catch (CentralRepoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContentTagTask implements Runnable {
|
||||
|
||||
private final CentralRepository dbManager;
|
||||
@ -430,9 +455,9 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
TagsManager tagsManager = openCase.getServices().getTagsManager();
|
||||
List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
|
||||
if (hasNotableTag(tags)) {
|
||||
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.BAD);
|
||||
setArtifactKnownStatus(dbManager, bbArtifact, TskData.FileKnown.BAD);
|
||||
} else {
|
||||
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.UNKNOWN);
|
||||
setArtifactKnownStatus(dbManager, bbArtifact, TskData.FileKnown.UNKNOWN);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to obtain tags manager for case.", ex);
|
||||
@ -450,24 +475,6 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
return ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the known status of a blackboard artifact in the central
|
||||
* repository.
|
||||
*
|
||||
* @param bbArtifact The blackboard artifact to set known status.
|
||||
* @param knownStatus The new known status.
|
||||
*/
|
||||
private void setArtifactKnownStatus(BlackboardArtifact bbArtifact, TskData.FileKnown knownStatus) {
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForSearch(bbArtifact);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
try {
|
||||
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} catch (CentralRepoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class TagDefinitionChangeTask implements Runnable {
|
||||
@ -528,12 +535,7 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
}
|
||||
//if the Correlation Attribute will have no tags with a status which would prevent the current status from being changed
|
||||
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<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForSearch(bbTag.getArtifact());
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
}
|
||||
setArtifactKnownStatus(CentralRepository.getInstance(), bbTag.getArtifact(), tagName.getKnownStatus());
|
||||
}
|
||||
}
|
||||
// Next update the files
|
||||
@ -568,7 +570,7 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
if (!hasTagWithConflictingKnownStatus) {
|
||||
Content taggedContent = contentTag.getContent();
|
||||
if (taggedContent instanceof AbstractFile) {
|
||||
final List<CorrelationAttributeInstance> eamArtifact = CorrelationAttributeUtil.makeCorrAttrsForSearch(taggedContent);
|
||||
final List<CorrelationAttributeInstance> 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 consistency with other makeCorrAttrsForSearch methods per 7852
|
||||
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact.get(0), tagName.getKnownStatus());
|
||||
@ -691,8 +693,8 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
for (OsAccountInstance osAccountInstance : addedOsAccountNew) {
|
||||
try {
|
||||
OsAccount osAccount = osAccountInstance.getOsAccount();
|
||||
CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(osAccount, osAccountInstance.getDataSource());
|
||||
if (correlationAttributeInstance == null) {
|
||||
List<CorrelationAttributeInstance> correlationAttributeInstances = CorrelationAttributeUtil.makeCorrAttrsForSearch(osAccountInstance);
|
||||
if (correlationAttributeInstances.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -700,19 +702,21 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
try {
|
||||
// Save to the database if requested
|
||||
if (IngestEventsListener.shouldCreateCrProperties()) {
|
||||
dbManager.addArtifactInstance(correlationAttributeInstance);
|
||||
for (CorrelationAttributeInstance correlationAttributeInstance : correlationAttributeInstances) {
|
||||
dbManager.addArtifactInstance(correlationAttributeInstance);
|
||||
}
|
||||
}
|
||||
|
||||
// Look up and create artifacts for previously seen accounts if requested
|
||||
if (IngestEventsListener.isFlagSeenDevices()) {
|
||||
CorrelationAttributeInstance.Type osAcctType = CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID);
|
||||
List<CorrelationAttributeInstance> previousOccurences = dbManager.getArtifactInstancesByTypeValue(osAcctType, correlationAttributeInstance.getCorrelationValue());
|
||||
List<CorrelationAttributeInstance> previousOccurences = dbManager.getArtifactInstancesByTypeValue(osAcctType, correlationAttributeInstances.get(0).getCorrelationValue());
|
||||
for (CorrelationAttributeInstance instance : previousOccurences) {
|
||||
if (!instance.getCorrelationCase().getCaseUUID().equals(correlationAttributeInstance.getCorrelationCase().getCaseUUID())) {
|
||||
if (!instance.getCorrelationCase().getCaseUUID().equals(correlationAttributeInstances.get(0).getCorrelationCase().getCaseUUID())) {
|
||||
SleuthkitCase tskCase = osAccount.getSleuthkitCase();
|
||||
Blackboard blackboard = tskCase.getBlackboard();
|
||||
|
||||
List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstances(osAcctType, correlationAttributeInstance.getCorrelationValue());
|
||||
List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstances(osAcctType, correlationAttributeInstances.get(0).getCorrelationValue());
|
||||
|
||||
// calculate score
|
||||
Score score;
|
||||
@ -737,7 +741,7 @@ public final class CaseEventListener implements PropertyChangeListener {
|
||||
osAcctType.getDisplayName()),
|
||||
new BlackboardAttribute(
|
||||
TSK_CORRELATION_VALUE, MODULE_NAME,
|
||||
correlationAttributeInstance.getCorrelationValue()),
|
||||
correlationAttributeInstances.get(0).getCorrelationValue()),
|
||||
new BlackboardAttribute(
|
||||
TSK_OTHER_CASES, MODULE_NAME,
|
||||
prevCases));
|
||||
|
@ -62,6 +62,7 @@ import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -229,6 +230,8 @@ public class IngestEventsListener {
|
||||
* @param originalArtifact Original artifact that we want to flag
|
||||
* @param caseDisplayNames List of case names artifact was previously seen
|
||||
* in
|
||||
* @param aType The correlation type.
|
||||
* @param value The correlation value.
|
||||
*/
|
||||
@NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
|
||||
"IngestEventsListener.prevCaseComment.text=Previous Case: "})
|
||||
@ -259,6 +262,8 @@ public class IngestEventsListener {
|
||||
* @param originalArtifact the artifact to create the "previously seen" item for
|
||||
* @param caseDisplayNames the case names the artifact was previously seen
|
||||
* in
|
||||
* @param aType The correlation type.
|
||||
* @param value The correlation value.
|
||||
*/
|
||||
@NbBundle.Messages({"IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)",
|
||||
"# {0} - typeName",
|
||||
@ -303,6 +308,8 @@ public class IngestEventsListener {
|
||||
*
|
||||
* @param originalArtifact the artifact to create the "previously unseen" item
|
||||
* for
|
||||
* @param aType The correlation type.
|
||||
* @param value The correlation value.
|
||||
*/
|
||||
static private void makeAndPostPreviouslyUnseenArtifact(BlackboardArtifact originalArtifact, CorrelationAttributeInstance.Type aType, String value) {
|
||||
Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(
|
||||
@ -319,6 +326,7 @@ public class IngestEventsListener {
|
||||
/**
|
||||
* Make an artifact to flag the passed in artifact.
|
||||
*
|
||||
* @param newArtifactType Type of artifact to create.
|
||||
* @param originalArtifact Artifact in current case we want to flag
|
||||
* @param attributesForNewArtifact Attributes to assign to the new artifact
|
||||
* @param configuration The configuration to be specified for the new artifact hit
|
||||
@ -540,7 +548,10 @@ public class IngestEventsListener {
|
||||
|
||||
for (BlackboardArtifact bbArtifact : bbArtifacts) {
|
||||
// makeCorrAttrToSave will filter out artifacts which should not be sources of CR data.
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsToSave(bbArtifact);
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = new ArrayList<>();
|
||||
if (bbArtifact instanceof DataArtifact){
|
||||
convertedArtifacts.addAll(CorrelationAttributeUtil.makeCorrAttrsToSave((DataArtifact)bbArtifact));
|
||||
}
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
try {
|
||||
// Only do something with this artifact if it's unique within the job
|
||||
|
@ -42,11 +42,13 @@ import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -135,7 +137,7 @@ public class AnnotationUtils {
|
||||
* @return The pair of artifact (or null if not present) and content (either
|
||||
* artifact parent content, the node content, or null).
|
||||
*/
|
||||
private static Pair<BlackboardArtifact, Content> getDisplayContent(Node node) {
|
||||
static DisplayTskItems getDisplayContent(Node node) {
|
||||
BlackboardArtifactItem<?> artItem = node.getLookup().lookup(BlackboardArtifactItem.class);
|
||||
BlackboardArtifact artifact = artItem == null ? null : artItem.getTskContent();
|
||||
|
||||
@ -143,16 +145,18 @@ public class AnnotationUtils {
|
||||
? artItem.getSourceContent()
|
||||
: node.getLookup().lookup(AbstractFile.class);
|
||||
|
||||
return Pair.of(artifact, content);
|
||||
return new DisplayTskItems(artifact, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the node is supported by the annotation viewer.
|
||||
*
|
||||
* @param node The node to display.
|
||||
*
|
||||
* @return True if the node is supported.
|
||||
*/
|
||||
public static boolean isSupported(Node node) {
|
||||
return getDisplayContent(node).getRight() != null;
|
||||
return getDisplayContent(node).getContent() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,9 +172,9 @@ public class AnnotationUtils {
|
||||
Document html = Jsoup.parse(EMPTY_HTML);
|
||||
Element body = html.getElementsByTag("body").first();
|
||||
|
||||
Pair<BlackboardArtifact, Content> displayPair = getDisplayContent(node);
|
||||
BlackboardArtifact artifact = displayPair.getLeft();
|
||||
Content srcContent = displayPair.getRight();
|
||||
DisplayTskItems displayItems = getDisplayContent(node);
|
||||
BlackboardArtifact artifact = displayItems.getArtifact();
|
||||
Content srcContent = displayItems.getContent();
|
||||
|
||||
boolean somethingWasRendered = false;
|
||||
if (artifact != null) {
|
||||
@ -380,9 +384,14 @@ public class AnnotationUtils {
|
||||
if (artifact == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<CorrelationAttributeInstance> instances = new ArrayList<>();
|
||||
if (artifact instanceof DataArtifact) {
|
||||
instances.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) artifact));
|
||||
} else if (artifact instanceof AnalysisResult) {
|
||||
instances.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) artifact));
|
||||
}
|
||||
|
||||
List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys = CorrelationAttributeUtil.makeCorrAttrsForSearch(artifact)
|
||||
.stream()
|
||||
List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys = instances.stream()
|
||||
.map(cai -> Pair.of(cai.getCorrelationType(), cai.getCorrelationValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@ -677,4 +686,39 @@ public class AnnotationUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The TSK items that are being displayed as deciphered from the netbeans
|
||||
* node.
|
||||
*/
|
||||
static class DisplayTskItems {
|
||||
|
||||
private final BlackboardArtifact artifact;
|
||||
private final Content content;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param artifact The artifact being displayed or null.
|
||||
* @param content The parent content or source file being displayed or
|
||||
* null.
|
||||
*/
|
||||
DisplayTskItems(BlackboardArtifact artifact, Content content) {
|
||||
this.artifact = artifact;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The selected artifact or null if no selected artifact.
|
||||
*/
|
||||
BlackboardArtifact getArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The parent content or source file being displayed or null.
|
||||
*/
|
||||
Content getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,16 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers.annotations;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Component;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import static org.openide.util.NbBundle.Messages;
|
||||
import org.openide.nodes.Node;
|
||||
@ -29,8 +35,19 @@ import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.openide.util.WeakListeners;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.contentviewers.annotations.AnnotationUtils.DisplayTskItems;
|
||||
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
|
||||
import org.sleuthkit.autopsy.contentviewers.utils.ViewerPriority;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Annotations view of file contents.
|
||||
@ -47,7 +64,75 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName());
|
||||
|
||||
private AnnotationWorker worker;
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
|
||||
Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
|
||||
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
|
||||
Case.Events.CONTENT_TAG_ADDED,
|
||||
Case.Events.CONTENT_TAG_DELETED,
|
||||
Case.Events.CR_COMMENT_CHANGED);
|
||||
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
|
||||
private static final Set<BlackboardArtifact.Type> ARTIFACT_TYPES_OF_INTEREST = ImmutableSet.of(
|
||||
BlackboardArtifact.Type.TSK_HASHSET_HIT,
|
||||
BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT
|
||||
);
|
||||
|
||||
private final PropertyChangeListener ingestEventListener = (evt) -> {
|
||||
Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
|
||||
Long curContentId = AnnotationsContentViewer.this.curContentId;
|
||||
|
||||
if (curArtifactId == null && curContentId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if it is a module data event
|
||||
if (IngestManager.IngestModuleEvent.DATA_ADDED.toString().equals(evt.getPropertyName())
|
||||
&& evt.getOldValue() instanceof ModuleDataEvent) {
|
||||
|
||||
ModuleDataEvent moduleDataEvent = (ModuleDataEvent) evt.getOldValue();
|
||||
|
||||
// if an artifact is relevant, refresh
|
||||
if (ARTIFACT_TYPES_OF_INTEREST.contains(moduleDataEvent.getBlackboardArtifactType())) {
|
||||
for (BlackboardArtifact artifact : moduleDataEvent.getArtifacts()) {
|
||||
if ((curArtifactId != null && artifact.getArtifactID() == curArtifactId)
|
||||
|| (curContentId != null && artifact.getObjectID() == curContentId)) {
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final PropertyChangeListener weakIngestEventListener = WeakListeners.propertyChange(ingestEventListener, null);
|
||||
|
||||
private final PropertyChangeListener caseEventListener = (evt) -> {
|
||||
Long curArtifactId = AnnotationsContentViewer.this.curArtifactId;
|
||||
Long curContentId = AnnotationsContentViewer.this.curContentId;
|
||||
|
||||
if (curArtifactId == null && curContentId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Long, Long> artifactContentId = getIdsFromEvent(evt);
|
||||
Long artifactId = artifactContentId.getLeft();
|
||||
Long contentId = artifactContentId.getRight();
|
||||
|
||||
// if there is a match of content id or artifact id and the event, refresh
|
||||
if ((curArtifactId != null && curArtifactId.equals(artifactId)) || (curContentId != null && curContentId.equals(contentId))) {
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
|
||||
private final PropertyChangeListener weakCaseEventListener = WeakListeners.propertyChange(caseEventListener, null);
|
||||
|
||||
private final Object updateLock = new Object();
|
||||
|
||||
private AnnotationWorker worker = null;
|
||||
private Node node;
|
||||
private Long curArtifactId;
|
||||
private Long curContentId;
|
||||
|
||||
/**
|
||||
* Creates an instance of AnnotationsContentViewer.
|
||||
@ -55,23 +140,138 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
public AnnotationsContentViewer() {
|
||||
initComponents();
|
||||
ContentViewerHtmlStyles.setupHtmlJTextPane(textPanel);
|
||||
registerListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers case event and ingest event listeners.
|
||||
*/
|
||||
private void registerListeners() {
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
unregisterListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters case event and ingest event listeners.
|
||||
*/
|
||||
private void unregisterListeners() {
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakCaseEventListener);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakIngestEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNode(Node node) {
|
||||
resetComponent();
|
||||
this.node = node;
|
||||
DisplayTskItems displayItems = AnnotationUtils.getDisplayContent(node);
|
||||
this.curArtifactId = displayItems.getArtifact() == null ? null : displayItems.getArtifact().getArtifactID();
|
||||
this.curContentId = displayItems.getContent() == null ? null : displayItems.getContent().getId();
|
||||
updateData(this.node, true);
|
||||
}
|
||||
|
||||
if (worker != null) {
|
||||
worker.cancel(true);
|
||||
worker = null;
|
||||
/**
|
||||
* Returns a pair of the artifact id (or null) and the content id (or null)
|
||||
* for the case event.
|
||||
*
|
||||
* @param evt The case event.
|
||||
*
|
||||
* @return A pair of the artifact id (or null) and the content id (or null)
|
||||
* for the case event.
|
||||
*/
|
||||
private static Pair<Long, Long> getIdsFromEvent(PropertyChangeEvent evt) {
|
||||
Case.Events eventType = null;
|
||||
try {
|
||||
eventType = Case.Events.valueOf(evt.getPropertyName());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logger.log(Level.SEVERE, "Unknown event type: " + evt.getPropertyName(), ex);
|
||||
return Pair.of(null, null);
|
||||
}
|
||||
|
||||
Long artifactId = null;
|
||||
Long contentId = null;
|
||||
|
||||
switch (eventType) {
|
||||
case BLACKBOARD_ARTIFACT_TAG_ADDED:
|
||||
if (evt instanceof BlackBoardArtifactTagAddedEvent) {
|
||||
BlackboardArtifact art = ((BlackBoardArtifactTagAddedEvent) evt).getAddedTag().getArtifact();
|
||||
artifactId = art.getArtifactID();
|
||||
contentId = art.getObjectID();
|
||||
}
|
||||
break;
|
||||
case BLACKBOARD_ARTIFACT_TAG_DELETED:
|
||||
if (evt instanceof BlackBoardArtifactTagDeletedEvent) {
|
||||
artifactId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getArtifactID();
|
||||
contentId = ((BlackBoardArtifactTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_ADDED:
|
||||
if (evt instanceof ContentTagAddedEvent) {
|
||||
contentId = ((ContentTagAddedEvent) evt).getAddedTag().getContent().getId();
|
||||
}
|
||||
break;
|
||||
case CONTENT_TAG_DELETED:
|
||||
if (evt instanceof ContentTagDeletedEvent) {
|
||||
contentId = ((ContentTagDeletedEvent) evt).getDeletedTagInfo().getContentID();
|
||||
}
|
||||
break;
|
||||
case CR_COMMENT_CHANGED:
|
||||
if (evt instanceof CommentChangedEvent) {
|
||||
long commentObjId = ((CommentChangedEvent) evt).getContentID();
|
||||
artifactId = commentObjId;
|
||||
contentId = commentObjId;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return Pair.of(artifactId, contentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the data displayed.
|
||||
*/
|
||||
private void refresh() {
|
||||
if (this.isVisible()) {
|
||||
updateData(this.node, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates data displayed in the viewer.
|
||||
*
|
||||
* @param node The node to use for data.
|
||||
* @param forceReset If true, forces a reset cancelling the previous worker
|
||||
* if one exists and clearing data in the component. If
|
||||
* false, only submits a worker if no previous worker is
|
||||
* running.
|
||||
*/
|
||||
private void updateData(Node node, boolean forceReset) {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
worker = new AnnotationWorker(node);
|
||||
worker.execute();
|
||||
if (forceReset) {
|
||||
resetComponent();
|
||||
}
|
||||
|
||||
synchronized (updateLock) {
|
||||
if (worker != null) {
|
||||
if (forceReset) {
|
||||
worker.cancel(true);
|
||||
worker = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
worker = new AnnotationWorker(node, forceReset);
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,6 +342,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
@Override
|
||||
public void resetComponent() {
|
||||
textPanel.setText("");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,9 +352,18 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
private class AnnotationWorker extends SwingWorker<String, Void> {
|
||||
|
||||
private final Node node;
|
||||
private final boolean resetCaretPosition;
|
||||
|
||||
AnnotationWorker(Node node) {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param node The node for which data will be fetched.
|
||||
* @param resetCaretPosition Whether or not to reset the caret position
|
||||
* when finished.
|
||||
*/
|
||||
AnnotationWorker(Node node, boolean resetCaretPosition) {
|
||||
this.node = node;
|
||||
this.resetCaretPosition = resetCaretPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -173,17 +383,25 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
if (!isCancelled()) {
|
||||
try {
|
||||
String text = get();
|
||||
ContentViewerHtmlStyles.setStyles(textPanel);
|
||||
textPanel.setText(text);
|
||||
|
||||
if (resetCaretPosition) {
|
||||
textPanel.setCaretPosition(0);
|
||||
}
|
||||
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get annotation information for node", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String text = get();
|
||||
ContentViewerHtmlStyles.setStyles(textPanel);
|
||||
textPanel.setText(text);
|
||||
textPanel.setCaretPosition(0);
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get annotation information for node", ex);
|
||||
synchronized (updateLock) {
|
||||
if (worker == this) {
|
||||
worker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,11 +334,6 @@
|
||||
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.report.modules.kml.KMLReport.getDefault"/>
|
||||
<attr name="position" intvalue="904"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-report-modules-stix-STIXReportModule.instance">
|
||||
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.report.GeneralReportModule"/>
|
||||
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.report.modules.stix.STIXReportModule.getDefault"/>
|
||||
<attr name="position" intvalue="910"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-report-modules-caseuco-CaseUcoReportModule.instance">
|
||||
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.report.GeneralReportModule"/>
|
||||
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.report.modules.caseuco.CaseUcoReportModule.getDefault"/>
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.actions.ViewArtifactAction;
|
||||
import org.sleuthkit.autopsy.actions.ViewOsAccountAction;
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
@ -27,6 +29,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -37,15 +40,22 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.Utilities;
|
||||
import org.openide.util.WeakListeners;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
||||
@ -74,17 +84,34 @@ import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
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.DerivedFile;
|
||||
import org.sleuthkit.datamodel.Directory;
|
||||
import org.sleuthkit.datamodel.File;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
import org.sleuthkit.datamodel.VolumeSystem;
|
||||
@ -130,7 +157,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
private Content srcContent;
|
||||
private volatile String translatedSourceName;
|
||||
private final String sourceObjTypeName;
|
||||
private final String srcContentShortDescription;
|
||||
|
||||
/*
|
||||
* A method has been provided to allow the injection of properties into this
|
||||
@ -241,7 +267,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
super(artifact, createLookup(artifact, false));
|
||||
this.artifact = artifact;
|
||||
this.artifactType = getType(artifact);
|
||||
|
||||
|
||||
srcContent = getSourceContentFromLookup(artifact);
|
||||
|
||||
if (srcContent == null) {
|
||||
@ -260,11 +286,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
|
||||
}
|
||||
sourceObjTypeName = getSourceObjType(srcContent);
|
||||
srcContentShortDescription = getContentShortDescription(srcContent);
|
||||
setDisplayNameBySourceContent();
|
||||
setName(Long.toString(artifact.getArtifactID()));
|
||||
String displayName = srcContent.getName();
|
||||
setDisplayName(displayName);
|
||||
setShortDescription(displayName);
|
||||
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
|
||||
}
|
||||
@ -308,11 +331,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
|
||||
}
|
||||
sourceObjTypeName = getSourceObjType(srcContent);
|
||||
srcContentShortDescription = getContentShortDescription(srcContent);
|
||||
setName(Long.toString(artifact.getArtifactID()));
|
||||
String displayName = srcContent.getName();
|
||||
setDisplayName(displayName);
|
||||
setShortDescription(displayName);
|
||||
setDisplayNameBySourceContent();
|
||||
String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
|
||||
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
|
||||
@ -484,52 +504,360 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
return this.artifact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of non null actions from the given possibly null options.
|
||||
*
|
||||
* @param items The items to purge of null items.
|
||||
*
|
||||
* @return The list of non-null actions.
|
||||
*/
|
||||
private List<Action> getNonNull(Action... items) {
|
||||
return Stream.of(items)
|
||||
.filter(i -> i != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.addAll(Arrays.asList(super.getActions(context)));
|
||||
// groupings of actions where each group will be separated by a divider
|
||||
List<List<Action>> actionsLists = new ArrayList<>();
|
||||
|
||||
/*
|
||||
* If the artifact represented by this node has a timestamp, add an
|
||||
* action to view it in the timeline.
|
||||
*/
|
||||
try {
|
||||
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)
|
||||
&& // don't show ViewArtifactInTimelineAction for AnalysisResults.
|
||||
(!(this.artifact instanceof AnalysisResult))) {
|
||||
// view artifact in timeline
|
||||
actionsLists.add(getNonNull(
|
||||
getTimelineArtifactAction(this.artifact)
|
||||
));
|
||||
|
||||
actionsList.add(new ViewArtifactInTimelineAction(artifact));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
// view associated file (TSK_PATH_ID attr) in directory and timeline
|
||||
actionsLists.add(getAssociatedFileActions(this.artifact, this.artifactType));
|
||||
|
||||
// view source content in directory and timeline
|
||||
actionsLists.add(getNonNull(
|
||||
getViewSrcContentAction(this.artifact, this.srcContent),
|
||||
getTimelineSrcContentAction(this.srcContent)
|
||||
));
|
||||
|
||||
// extract with password from encrypted file
|
||||
actionsLists.add(getNonNull(
|
||||
getExtractWithPasswordAction(this.srcContent)
|
||||
));
|
||||
|
||||
// menu options for artifact with report parent
|
||||
if (this.srcContent instanceof Report) {
|
||||
actionsLists.add(DataModelActionsFactory.getActions(this.srcContent, false));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the artifact represented by this node is linked to a file via a
|
||||
* TSK_PATH_ID attribute, add an action to view the file in the
|
||||
* timeline.
|
||||
*/
|
||||
Node parentFileNode = getParentFileNode(srcContent);
|
||||
int selectedFileCount = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).size();
|
||||
int selectedArtifactCount = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactItem.class).size();
|
||||
|
||||
// view source content if source content is some sort of file
|
||||
actionsLists.add(getSrcContentViewerActions(parentFileNode, selectedFileCount));
|
||||
|
||||
// extract / export if source content is some sort of file
|
||||
if (parentFileNode != null) {
|
||||
actionsLists.add(Arrays.asList(ExtractAction.getInstance(), ExportCSVAction.getInstance()));
|
||||
}
|
||||
|
||||
// file and result tagging
|
||||
actionsLists.add(getTagActions(parentFileNode != null, this.artifact, selectedFileCount, selectedArtifactCount));
|
||||
|
||||
// menu extension items (i.e. add to central repository)
|
||||
actionsLists.add(ContextMenuExtensionPoint.getActions());
|
||||
|
||||
// netbeans default items (i.e. properties)
|
||||
actionsLists.add(Arrays.asList(super.getActions(context)));
|
||||
|
||||
return actionsLists.stream()
|
||||
// remove any empty lists
|
||||
.filter((lst) -> lst != null && !lst.isEmpty())
|
||||
// add in null between each list group
|
||||
.flatMap(lst -> Stream.concat(Stream.of((Action) null), lst.stream()))
|
||||
// skip the first null
|
||||
.skip(1)
|
||||
.toArray(sz -> new Action[sz]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the artifact based on the artifact type to be used
|
||||
* with the associated file string in a right click menu.
|
||||
*
|
||||
* @param artifactType The artifact type.
|
||||
*
|
||||
* @return The artifact type name.
|
||||
*/
|
||||
@Messages({
|
||||
"BlackboardArtifactNode_getAssociatedTypeStr_webCache=Cached File",
|
||||
"BlackboardArtifactNode_getAssociatedTypeStr_webDownload=Downloaded File",
|
||||
"BlackboardArtifactNode_getAssociatedTypeStr_associated=Associated File",})
|
||||
private String getAssociatedTypeStr(BlackboardArtifact.Type artifactType) {
|
||||
if (BlackboardArtifact.Type.TSK_WEB_CACHE.equals(artifactType)) {
|
||||
return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webCache();
|
||||
} else if (BlackboardArtifact.Type.TSK_WEB_DOWNLOAD.equals(artifactType)) {
|
||||
return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_webDownload();
|
||||
} else {
|
||||
return Bundle.BlackboardArtifactNode_getAssociatedTypeStr_associated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name to represent the type of the content (file, data
|
||||
* artifact, os account, item).
|
||||
*
|
||||
* @param content The content.
|
||||
*
|
||||
* @return The name of the type of content.
|
||||
*/
|
||||
@Messages({
|
||||
"BlackboardArtifactNode_getViewSrcContentAction_type_File=File",
|
||||
"BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact=Data Artifact",
|
||||
"BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount=OS Account",
|
||||
"BlackboardArtifactNode_getViewSrcContentAction_type_unknown=Item"
|
||||
})
|
||||
private String getContentTypeStr(Content content) {
|
||||
if (content instanceof AbstractFile) {
|
||||
return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_File();
|
||||
} else if (content instanceof DataArtifact) {
|
||||
return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact();
|
||||
} else if (content instanceof OsAccount) {
|
||||
return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount();
|
||||
} else {
|
||||
return Bundle.BlackboardArtifactNode_getViewSrcContentAction_type_unknown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actions for navigating to an associated file in the directory or
|
||||
* in the timeline.
|
||||
*
|
||||
* @param artifact The artifact whose associated file will be
|
||||
* identified.
|
||||
* @param artifactType The type of artifact.
|
||||
*
|
||||
* @return The actions or an empty list.
|
||||
*/
|
||||
@Messages({
|
||||
"# {0} - type",
|
||||
"BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction=View {0} in Directory",
|
||||
"# {0} - type",
|
||||
"BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction=View {0} in Timeline..."
|
||||
})
|
||||
private List<Action> getAssociatedFileActions(BlackboardArtifact artifact, BlackboardArtifact.Type artifactType) {
|
||||
try {
|
||||
AbstractFile linkedFile = findLinked(artifact);
|
||||
if (linkedFile != null) {
|
||||
actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
|
||||
AbstractFile associatedFile = findLinked(artifact);
|
||||
if (associatedFile != null) {
|
||||
return Arrays.asList(
|
||||
new ViewContextAction(
|
||||
Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction(
|
||||
getAssociatedTypeStr(artifactType)),
|
||||
associatedFile),
|
||||
new ViewFileInTimelineAction(associatedFile,
|
||||
Bundle.BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction(
|
||||
getAssociatedTypeStr(artifactType)))
|
||||
);
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an action to navigate to src content in tree hierarchy.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @param content The content.
|
||||
*
|
||||
* @return The action or null if no action derived.
|
||||
*/
|
||||
@Messages({
|
||||
"# {0} - contentType",
|
||||
"BlackboardArtifactNode_getSrcContentAction_actionDisplayName=View Source {0} in Directory"
|
||||
})
|
||||
private Action getViewSrcContentAction(BlackboardArtifact artifact, Content content) {
|
||||
if (content instanceof DataArtifact) {
|
||||
return new ViewArtifactAction(
|
||||
(BlackboardArtifact) content,
|
||||
Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
|
||||
getContentTypeStr(content)));
|
||||
} else if (content instanceof OsAccount) {
|
||||
return new ViewOsAccountAction(
|
||||
(OsAccount) content,
|
||||
Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
|
||||
getContentTypeStr(content)));
|
||||
} else if (content instanceof AbstractFile || artifact instanceof DataArtifact) {
|
||||
return new ViewContextAction(
|
||||
Bundle.BlackboardArtifactNode_getSrcContentAction_actionDisplayName(
|
||||
getContentTypeStr(content)),
|
||||
content);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Node representing the file content if the content is indeed
|
||||
* some sort of file. Otherwise, return null.
|
||||
*
|
||||
* @param content The content.
|
||||
*
|
||||
* @return The file node or null if not a file.
|
||||
*/
|
||||
private Node getParentFileNode(Content content) {
|
||||
if (content instanceof File) {
|
||||
return new FileNode((AbstractFile) content);
|
||||
} else if (content instanceof Directory) {
|
||||
return new DirectoryNode((Directory) content);
|
||||
} else if (content instanceof VirtualDirectory) {
|
||||
return new VirtualDirectoryNode((VirtualDirectory) content);
|
||||
} else if (content instanceof LocalDirectory) {
|
||||
return new LocalDirectoryNode((LocalDirectory) content);
|
||||
} else if (content instanceof LayoutFile) {
|
||||
return new LayoutFileNode((LayoutFile) content);
|
||||
} else if (content instanceof LocalFile || content instanceof DerivedFile) {
|
||||
return new LocalFileNode((AbstractFile) content);
|
||||
} else if (content instanceof SlackFile) {
|
||||
return new SlackFileNode((AbstractFile) content);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actions for extracting content from file or null if not possible.
|
||||
*
|
||||
* @param srcContent The source content.
|
||||
*
|
||||
* @return The action or null if not appropriate source content.
|
||||
*/
|
||||
private Action getExtractWithPasswordAction(Content srcContent) {
|
||||
if ((srcContent instanceof AbstractFile)
|
||||
&& FileTypeExtensions.getArchiveExtensions()
|
||||
.contains("." + ((AbstractFile) srcContent).getNameExtension().toLowerCase())) {
|
||||
try {
|
||||
if (srcContent.getArtifacts(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED.getTypeID()).size() > 0) {
|
||||
return new ExtractArchiveWithPasswordAction((AbstractFile) srcContent);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the source content of the artifact represented by this node is a
|
||||
* file, add an action to view the file in the data source tree.
|
||||
*/
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class
|
||||
);
|
||||
if (null != file) {
|
||||
actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tag actions.
|
||||
*
|
||||
* @param hasSrcFile Whether or not the artifact has a source
|
||||
* file.
|
||||
* @param artifact This artifact.
|
||||
* @param selectedFileCount The count of selected files.
|
||||
* @param selectedArtifactCount The count of selected artifacts.
|
||||
*
|
||||
* @return The tag actions.
|
||||
*/
|
||||
private List<Action> getTagActions(boolean hasSrcFile, BlackboardArtifact artifact, int selectedFileCount, int selectedArtifactCount) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
// don't show AddContentTagAction for data artifacts.
|
||||
if (hasSrcFile && !(artifact instanceof DataArtifact)) {
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
}
|
||||
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
|
||||
// don't show DeleteFileContentTagAction for data artifacts.
|
||||
if (hasSrcFile && (!(artifact instanceof DataArtifact)) && (selectedFileCount == 1)) {
|
||||
actionsList.add(DeleteFileContentTagAction.getInstance());
|
||||
}
|
||||
|
||||
if (selectedArtifactCount == 1) {
|
||||
actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns actions to view src content in a different viewer or window.
|
||||
*
|
||||
* @param srcFileNode The source file node or null if no source file.
|
||||
* @param selectedFileCount The number of selected files.
|
||||
*
|
||||
* @return The list of actions or an empty list.
|
||||
*/
|
||||
@Messages({
|
||||
"BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin=View Item in New Window",
|
||||
"BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer=Open in External Viewer Ctrl+E"
|
||||
})
|
||||
private List<Action> getSrcContentViewerActions(Node srcFileNode, int selectedFileCount) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
if (srcFileNode != null) {
|
||||
actionsList.add(new NewWindowViewAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin(), srcFileNode));
|
||||
if (selectedFileCount == 1) {
|
||||
actionsList.add(new ExternalViewerAction(Bundle.BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer(), srcFileNode));
|
||||
} else {
|
||||
actionsList.add(ExternalViewerShortcutAction.getInstance());
|
||||
}
|
||||
}
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the source content of the artifact represented by this node is a file,
|
||||
* returns an action to view the file in the data source tree.
|
||||
*
|
||||
* @param srcContent The src content to navigate to in the timeline action.
|
||||
*
|
||||
* @return The src content navigation action or null.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - contentType",
|
||||
"BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName=View Source {0} in Timeline... "
|
||||
})
|
||||
private Action getTimelineSrcContentAction(Content srcContent) {
|
||||
if (srcContent instanceof AbstractFile) {
|
||||
return new ViewFileInTimelineAction((AbstractFile) srcContent,
|
||||
Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
|
||||
getContentTypeStr(srcContent)));
|
||||
} else if (srcContent instanceof DataArtifact) {
|
||||
try {
|
||||
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp((BlackboardArtifact) srcContent)) {
|
||||
return new ViewArtifactInTimelineAction((BlackboardArtifact) srcContent,
|
||||
Bundle.BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName(
|
||||
getContentTypeStr(srcContent)));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting source data artifact timestamp (artifact objID={0})", srcContent.getId()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the artifact represented by this node has a timestamp, an action to
|
||||
* view it in the timeline.
|
||||
*
|
||||
* @param art The artifact for timeline navigation action.
|
||||
*
|
||||
* @return The action or null if no action should exist.
|
||||
*/
|
||||
@Messages({
|
||||
"BlackboardArtifactNode_getTimelineArtifactAction_displayName=View Selected Item in Timeline... "
|
||||
})
|
||||
private Action getTimelineArtifactAction(BlackboardArtifact art) {
|
||||
try {
|
||||
// don't show ViewArtifactInTimelineAction for AnalysisResults.
|
||||
if (!(art instanceof AnalysisResult) && ViewArtifactInTimelineAction.hasSupportedTimeStamp(art)) {
|
||||
return new ViewArtifactInTimelineAction(art, Bundle.BlackboardArtifactNode_getTimelineArtifactAction_displayName());
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", art.getId()), ex); //NON-NLS
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -543,8 +871,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"BlackboardArtifactNode.createSheet.srcFile.name=Source File",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.displayName=Source File",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.name=Source Name",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.displayName=Source Name",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
|
||||
@ -569,6 +897,18 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the name of the source content of the artifact represented by
|
||||
* this node to the sheet. The value of this property is the same as
|
||||
* the display name of the node and this a "special" property that
|
||||
* displays the node's icon as well as the display name.
|
||||
*/
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
|
||||
NO_DESCR,
|
||||
getDisplayName()));
|
||||
|
||||
boolean scoHasBeenAdded = false;
|
||||
if (artifact instanceof AnalysisResult
|
||||
@ -576,20 +916,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
|| artifactType.getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())) {
|
||||
updateSheetForAnalysisResult((AnalysisResult) artifact, sheetSet);
|
||||
scoHasBeenAdded = true;
|
||||
} else {
|
||||
/*
|
||||
* Add the name of the source content of the artifact represented by
|
||||
* this node to the sheet. The value of this property is the same as
|
||||
* the display name of the node and this a "special" property that
|
||||
* displays the node's icon as well as the display name.
|
||||
*/
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
|
||||
NO_DESCR,
|
||||
getDisplayName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
|
||||
/*
|
||||
* If machine translation is configured, add the original name of
|
||||
@ -624,13 +952,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
if (attribute != null) {
|
||||
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.artifactType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.artifactType.displayName"),
|
||||
NO_DESCR,
|
||||
associatedArtifact.getDisplayName()));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
|
||||
NO_DESCR,
|
||||
associatedArtifact.getShortDescription()));
|
||||
}
|
||||
@ -680,13 +1012,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.ext.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.ext.displayName"),
|
||||
NO_DESCR,
|
||||
ext));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.mimeType.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.mimeType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.mimeType.displayName"),
|
||||
NO_DESCR,
|
||||
actualMimeType));
|
||||
}
|
||||
@ -706,8 +1042,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
|
||||
if (sourcePath.isEmpty() == false) {
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.filePath.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.filePath.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.filePath.displayName"),
|
||||
NO_DESCR,
|
||||
sourcePath));
|
||||
}
|
||||
@ -720,28 +1058,38 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
|
||||
AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileModifiedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileModifiedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileChangedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileAccessedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileCreatedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"ContentTagNode.createSheet.fileSize.displayName"),
|
||||
"",
|
||||
file == null ? "" : file.getSize()));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
@ -766,8 +1114,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
|
||||
if (dataSourceStr.isEmpty() == false) {
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.dataSrc.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.dataSrc.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.dataSrc.displayName"),
|
||||
NO_DESCR,
|
||||
dataSourceStr));
|
||||
}
|
||||
@ -791,14 +1141,18 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.fileSize.displayName"),
|
||||
NO_DESCR,
|
||||
size));
|
||||
sheetSet
|
||||
.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.path.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class,
|
||||
"BlackboardArtifactNode.createSheet.path.displayName"),
|
||||
NO_DESCR,
|
||||
path));
|
||||
}
|
||||
@ -833,9 +1187,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* corresponding commented correlation attribute instance in the central
|
||||
* repository.
|
||||
*
|
||||
* @param tags The tags applied to the artifact and its source content.
|
||||
* @param attribute A correlation attribute instance for the central
|
||||
* repository lookup.
|
||||
* @param tags The tags applied to the artifact and its source
|
||||
* content.
|
||||
* @param attributes A correlation attribute instance for the central
|
||||
* repository lookup.
|
||||
*
|
||||
* @return The value of the comment property.
|
||||
*/
|
||||
@ -1065,13 +1420,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* @param result The AnalysisResult the sheet is being created.
|
||||
* @param sheetSet The sheetSet to add the values to.
|
||||
*/
|
||||
private void updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet) {
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
|
||||
Bundle.BlackboardArtifactNode_analysisSheet_soureName_name(),
|
||||
NO_DESCR,
|
||||
srcContentShortDescription));
|
||||
|
||||
private void updateSheetForAnalysisResult(AnalysisResult result, Sheet.Set sheetSet) {
|
||||
addSCOColumns(sheetSet);
|
||||
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
@ -1104,7 +1453,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
NO_DESCR,
|
||||
result.getJustification()));
|
||||
}
|
||||
|
||||
|
||||
private void addSCOColumns(Sheet.Set sheetSet) {
|
||||
if (!UserPreferences.getHideSCOColumns()) {
|
||||
/*
|
||||
@ -1137,12 +1486,12 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
|
||||
/**
|
||||
* Returns a displayable type string for the given content object.
|
||||
*
|
||||
* If the content object is a artifact of a custom type then this method
|
||||
* may cause a DB call BlackboardArtifact.getType
|
||||
*
|
||||
*
|
||||
* If the content object is a artifact of a custom type then this method may
|
||||
* cause a DB call BlackboardArtifact.getType
|
||||
*
|
||||
* @param source The object to determine the type of.
|
||||
*
|
||||
*
|
||||
* @return A string representing the content type.
|
||||
*/
|
||||
private String getSourceObjType(Content source) {
|
||||
@ -1170,28 +1519,50 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a short description for the given content object.
|
||||
*
|
||||
*
|
||||
* @param content The content object.
|
||||
*
|
||||
* @return A short description\label.
|
||||
*
|
||||
* @return A short description/label.
|
||||
*/
|
||||
private String getContentShortDescription(Content content) {
|
||||
if(content != null) {
|
||||
if(content instanceof BlackboardArtifact) {
|
||||
try{
|
||||
return ((BlackboardArtifact)content).getShortDescription();
|
||||
if (content != null) {
|
||||
if (content instanceof BlackboardArtifact) {
|
||||
try {
|
||||
return ((BlackboardArtifact) content).getShortDescription();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get short description for artifact id=" + content.getId(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content.getName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the displayName and short description for the node.
|
||||
*/
|
||||
private void setDisplayNameBySourceContent() {
|
||||
if(srcContent instanceof BlackboardArtifact) {
|
||||
try {
|
||||
setDisplayName(((BlackboardArtifact)srcContent).getShortDescription());
|
||||
} catch (TskCoreException ex) {
|
||||
// Log the error, but set the display name to
|
||||
// Content.getName so there is something visible to the user.
|
||||
logger.log(Level.WARNING, "Failed to get short description for artifact id = " + srcContent.getId(), ex);
|
||||
setDisplayName(srcContent.getName());
|
||||
}
|
||||
} else if(srcContent instanceof OsAccount) {
|
||||
setDisplayName(((OsAccount)srcContent).getAddr().orElse(srcContent.getName()));
|
||||
} else {
|
||||
setDisplayName(srcContent.getName());
|
||||
}
|
||||
|
||||
setShortDescription(getDisplayName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the score property for the artifact represented by this node to the
|
||||
|
@ -84,6 +84,24 @@ BlackboardArtifactNode_analysisSheet_justifaction_name=Justification
|
||||
BlackboardArtifactNode_analysisSheet_score_name=Score
|
||||
BlackboardArtifactNode_analysisSheet_sourceType_name=Source Type
|
||||
BlackboardArtifactNode_analysisSheet_soureName_name=Source Name
|
||||
# {0} - type
|
||||
BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileAction=View {0} in Directory
|
||||
# {0} - type
|
||||
BlackboardArtifactNode_getAssociatedFileActions_viewAssociatedFileInTimelineAction=View {0} in Timeline...
|
||||
BlackboardArtifactNode_getAssociatedTypeStr_associated=Associated File
|
||||
BlackboardArtifactNode_getAssociatedTypeStr_webCache=Cached File
|
||||
BlackboardArtifactNode_getAssociatedTypeStr_webDownload=Downloaded File
|
||||
# {0} - contentType
|
||||
BlackboardArtifactNode_getSrcContentAction_actionDisplayName=View Source {0} in Directory
|
||||
BlackboardArtifactNode_getSrcContentViewerActions_openInExtViewer=Open in External Viewer Ctrl+E
|
||||
BlackboardArtifactNode_getSrcContentViewerActions_viewInNewWin=View Item in New Window
|
||||
BlackboardArtifactNode_getTimelineArtifactAction_displayName=View Selected Item in Timeline...
|
||||
# {0} - contentType
|
||||
BlackboardArtifactNode_getTimelineSrcContentAction_actionDisplayName=View Source {0} in Timeline...
|
||||
BlackboardArtifactNode_getViewSrcContentAction_type_DataArtifact=Data Artifact
|
||||
BlackboardArtifactNode_getViewSrcContentAction_type_File=File
|
||||
BlackboardArtifactNode_getViewSrcContentAction_type_OSAccount=OS Account
|
||||
BlackboardArtifactNode_getViewSrcContentAction_type_unknown=Item
|
||||
BlackboardArtifactTagNode.createSheet.userName.text=User Name
|
||||
BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result
|
||||
Category.five=CAT-5: Non-pertinent
|
||||
|
@ -99,7 +99,6 @@ public abstract class DisplayableItemNode extends AbstractNode {
|
||||
* operation on this artifact type and return some object as the result of
|
||||
* the operation.
|
||||
*
|
||||
* @param <T> 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.
|
||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
@ -29,6 +30,10 @@ import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
|
||||
/**
|
||||
* Background task to get Score, Comment and Occurrences values for an Abstract
|
||||
@ -62,7 +67,18 @@ class GetSCOTask implements Runnable {
|
||||
//getting the correlation attribute and setting the comment column is done before the eamdb isEnabled check
|
||||
//because the Comment column will reflect the presence of comments in the CR when the CR is enabled, but reflect tag comments regardless
|
||||
String description = Bundle.GetSCOTask_occurrences_defaultDescription();
|
||||
List<CorrelationAttributeInstance> listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsForSearch(contentNode.getContent());
|
||||
|
||||
List<CorrelationAttributeInstance> listOfPossibleAttributes = new ArrayList<>();
|
||||
Content contentFromNode = contentNode.getContent();
|
||||
if (contentFromNode instanceof AbstractFile) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AbstractFile) contentFromNode));
|
||||
} else if (contentFromNode instanceof AnalysisResult) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) contentFromNode));
|
||||
} else if (contentFromNode instanceof DataArtifact) {
|
||||
listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) contentFromNode));
|
||||
} else {
|
||||
//JIRA-TODO : add code for Jira-7938 OsAccounts
|
||||
}
|
||||
scoData.setComment(contentNode.getCommentProperty(tags, listOfPossibleAttributes));
|
||||
CorrelationAttributeInstance corInstance = null;
|
||||
if (CentralRepository.isEnabled()) {
|
||||
|
@ -60,10 +60,20 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
|
||||
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
private static final String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT";
|
||||
private static final String LIST_NAME = Bundle.OsAccount_listNode_name();
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long filteringDSObjId;
|
||||
|
||||
/**
|
||||
* Returns the name of the OsAccountListNode to be used for id purposes.
|
||||
*
|
||||
* @return The name of the OsAccountListNode to be used for id purposes.
|
||||
*/
|
||||
public static String getListName() {
|
||||
return LIST_NAME;
|
||||
}
|
||||
|
||||
public OsAccounts(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
@ -91,8 +101,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
public OsAccountListNode() {
|
||||
super(Children.create(new OsAccountNodeFactory(), true));
|
||||
setName(Bundle.OsAccount_listNode_name());
|
||||
setDisplayName(Bundle.OsAccount_listNode_name());
|
||||
setName(LIST_NAME);
|
||||
setDisplayName(LIST_NAME);
|
||||
setIconBaseWithExtension("org/sleuthkit/autopsy/images/os-account.png");
|
||||
}
|
||||
|
||||
@ -134,7 +144,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(listener, null);
|
||||
|
||||
@Override
|
||||
@ -143,7 +153,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_ADDED), weakPcl);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNTS_ADDED, Case.Events.OS_ACCOUNTS_DELETED), listener);
|
||||
|
@ -170,7 +170,7 @@ public class TypesSummary {
|
||||
* Constructor that accepts FileTypeCategory.
|
||||
*
|
||||
* @param label The label for this slice.
|
||||
* @param mimeTypes The mime types associated with this slice.
|
||||
* @param fileCategory The category associated with this slice.
|
||||
* @param color The color associated with this slice.
|
||||
*/
|
||||
public FileTypeCategoryData(String label, FileTypeUtils.FileTypeCategory fileCategory, Color color) {
|
||||
|
@ -66,7 +66,7 @@ GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX P
|
||||
GeolocationPanel_unknownRow_title=Unknown
|
||||
PastCasesPanel_caseColumn_title=Case
|
||||
PastCasesPanel_countColumn_title=Count
|
||||
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
|
||||
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable Items at Time Of Ingest
|
||||
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.
|
||||
PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices
|
||||
RecentFilesPanel_attachmentsTable_tabName=Recent Attachments
|
||||
@ -75,7 +75,7 @@ RecentFilesPanel_col_header_domain=Domain
|
||||
RecentFilesPanel_col_header_path=Path
|
||||
RecentFilesPanel_col_header_sender=Sender
|
||||
RecentFilesPanel_docsTable_tabName=Recently Opened Documents
|
||||
RecentFilesPanel_downloadsTable_tabName=Recently Downloads
|
||||
RecentFilesPanel_downloadsTable_tabName=Recent Downloads
|
||||
RecentFilesPanel_no_open_documents=No recently open documents found.
|
||||
SizeRepresentationUtil_units_bytes=bytes
|
||||
SizeRepresentationUtil_units_gigabytes=GB
|
||||
|
@ -41,7 +41,7 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
"PastCasesPanel_caseColumn_title=Case",
|
||||
"PastCasesPanel_countColumn_title=Count",
|
||||
"PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.",
|
||||
"PastCasesPanel_notableFileTable_tabName=Cases with Common Notable",
|
||||
"PastCasesPanel_notableFileTable_tabName=Cases with Common Notable Items at Time Of Ingest",
|
||||
"PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices",})
|
||||
public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
|
@ -47,7 +47,7 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
*/
|
||||
@Messages({
|
||||
"RecentFilesPanel_docsTable_tabName=Recently Opened Documents",
|
||||
"RecentFilesPanel_downloadsTable_tabName=Recently Downloads",
|
||||
"RecentFilesPanel_downloadsTable_tabName=Recent Downloads",
|
||||
"RecentFilesPanel_attachmentsTable_tabName=Recent Attachments",})
|
||||
public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
|
@ -22,8 +22,7 @@ import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyVetoException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
@ -34,53 +33,25 @@ import org.openide.nodes.FilterNode;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
|
||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
|
||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.FileTypeExtensions;
|
||||
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.InstanceCountNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.InstanceCaseNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.InstanceDataSourceNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributeValueNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.CentralRepoCommonAttributeInstanceNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LayoutFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LocalFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports;
|
||||
import org.sleuthkit.autopsy.datamodel.SlackFileNode;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.CaseDBCommonAttributeInstanceNode;
|
||||
import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode;
|
||||
import static org.sleuthkit.autopsy.directorytree.Bundle.DataResultFilterNode_viewSourceArtifact_text;
|
||||
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DerivedFile;
|
||||
import org.sleuthkit.datamodel.Directory;
|
||||
import org.sleuthkit.datamodel.File;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -98,8 +69,8 @@ public class DataResultFilterNode extends FilterNode {
|
||||
// Assumptions are made in GetPreferredActionsDisplayableItemNodeVisitor that
|
||||
// sourceEm is the directory tree explorer manager.
|
||||
private final ExplorerManager sourceEm;
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Constructs a node used to wrap another node before passing it to the
|
||||
* result viewers. The wrapper node defines the actions associated with the
|
||||
* wrapped node and may filter out some of its children.
|
||||
@ -107,7 +78,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
* @param node The node to wrap.
|
||||
*/
|
||||
public DataResultFilterNode(Node node) {
|
||||
this(node, null);
|
||||
this(node, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,18 +211,18 @@ public class DataResultFilterNode extends FilterNode {
|
||||
protected Node[] createNodes(Node key) {
|
||||
// if displaying the results from the Data Source tree
|
||||
// filter out artifacts
|
||||
|
||||
|
||||
// In older versions of Autopsy, attachments were children of email/message artifacts
|
||||
// and hence email/messages with attachments are shown in the tree data source tree,
|
||||
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
|
||||
if (art != null && filterArtifacts
|
||||
&& ((FilterNodeUtils.showMessagesInDatasourceTree() == false)
|
||||
|| (FilterNodeUtils.showMessagesInDatasourceTree()
|
||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|
||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()))) {
|
||||
|| (FilterNodeUtils.showMessagesInDatasourceTree()
|
||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|
||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()))) {
|
||||
return new Node[]{};
|
||||
}
|
||||
|
||||
|
||||
return new Node[]{new DataResultFilterNode(key, sourceEm)};
|
||||
}
|
||||
}
|
||||
@ -265,137 +236,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
|
||||
@Override
|
||||
public List<Action> visit(BlackboardArtifactNode ban) {
|
||||
//set up actions for artifact node based on its Content object
|
||||
//TODO all actions need to be consolidated in single place!
|
||||
//they should be set in individual Node subclass and using a utility to get Actions per Content sub-type
|
||||
// TODO UPDATE: There is now a DataModelActionsFactory utility;
|
||||
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
//merge predefined specific node actions if bban subclasses have their own
|
||||
for (Action a : ban.getActions(true)) {
|
||||
actionsList.add(a);
|
||||
}
|
||||
|
||||
//Add seperator between the decorated actions and the actions from the node itself.
|
||||
actionsList.add(null);
|
||||
BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class);
|
||||
final int artifactTypeID = ba.getArtifactTypeID();
|
||||
|
||||
if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()
|
||||
|| artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
|
||||
if (ban.getLookup().lookup(AbstractFile.class) != null) {
|
||||
// We only want the "View File in Directory" actions if we have a file...it is
|
||||
// possible that we have a keyword hit on a Report.
|
||||
actionsList.add(new ViewContextAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), ban));
|
||||
}
|
||||
} else if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
|
||||
try {
|
||||
if (ba.getAttribute(BlackboardAttribute.Type.TSK_ASSOCIATED_ARTIFACT) != null) {
|
||||
//action to go to the source artifact
|
||||
actionsList.add(new ViewSourceArtifactAction(DataResultFilterNode_viewSourceArtifact_text(), ba));
|
||||
|
||||
// action to go to the source file of the artifact
|
||||
actionsList.add(new ViewContextAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error looking up attributes for artifact with ID=" + ba.getId());
|
||||
}
|
||||
} else {
|
||||
// if the artifact links to another file, add an action to go to
|
||||
// that file
|
||||
Content c = findLinked(ban);
|
||||
if (c != null) {
|
||||
actionsList.add(new ViewContextAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c));
|
||||
}
|
||||
// action to go to the source file of the artifact
|
||||
Content fileContent = ban.getLookup().lookup(AbstractFile.class);
|
||||
if (fileContent == null) {
|
||||
Content content = ban.getLookup().lookup(Content.class);
|
||||
actionsList.add(new ViewContextAction("View Source Content in Directory", content));
|
||||
} else {
|
||||
actionsList.add(new ViewContextAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban));
|
||||
}
|
||||
}
|
||||
Content c = ban.getLookup().lookup(File.class);
|
||||
Node n = null;
|
||||
if (c != null) {
|
||||
n = new FileNode((AbstractFile) c);
|
||||
} else if ((c = ban.getLookup().lookup(Directory.class)) != null) {
|
||||
n = new DirectoryNode((Directory) c);
|
||||
} else if ((c = ban.getLookup().lookup(VirtualDirectory.class)) != null) {
|
||||
n = new VirtualDirectoryNode((VirtualDirectory) c);
|
||||
} else if ((c = ban.getLookup().lookup(LocalDirectory.class)) != null) {
|
||||
n = new LocalDirectoryNode((LocalDirectory) c);
|
||||
} else if ((c = ban.getLookup().lookup(LayoutFile.class)) != null) {
|
||||
n = new LayoutFileNode((LayoutFile) c);
|
||||
} else if ((c = ban.getLookup().lookup(LocalFile.class)) != null
|
||||
|| (c = ban.getLookup().lookup(DerivedFile.class)) != null) {
|
||||
n = new LocalFileNode((AbstractFile) c);
|
||||
if (FileTypeExtensions.getArchiveExtensions().contains("." + ((AbstractFile) c).getNameExtension().toLowerCase())) {
|
||||
try {
|
||||
if (c.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED).size() > 0) {
|
||||
actionsList.add(new ExtractArchiveWithPasswordAction((AbstractFile) c));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
|
||||
}
|
||||
}
|
||||
} else if ((c = ban.getLookup().lookup(SlackFile.class)) != null) {
|
||||
n = new SlackFileNode((SlackFile) c);
|
||||
} else if ((c = ban.getLookup().lookup(Report.class)) != null) {
|
||||
actionsList.addAll(DataModelActionsFactory.getActions(c, false));
|
||||
}
|
||||
if (n != null) {
|
||||
final Collection<AbstractFile> selectedFilesList
|
||||
= new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new NewWindowViewAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewInNewWin.text"), n));
|
||||
if (selectedFilesList.size() == 1) {
|
||||
actionsList.add(new ExternalViewerAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.openInExtViewer.text"), n));
|
||||
} else {
|
||||
actionsList.add(ExternalViewerShortcutAction.getInstance());
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.add(null); // creates a menu separator
|
||||
|
||||
// don't show AddContentTagAction for data artifacts.
|
||||
if (!(ban.getArtifact() instanceof DataArtifact)) {
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
}
|
||||
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
|
||||
// don't show DeleteFileContentTagAction for data artifacts.
|
||||
if ((!(ban.getArtifact() instanceof DataArtifact)) && (selectedFilesList.size() == 1)) {
|
||||
actionsList.add(DeleteFileContentTagAction.getInstance());
|
||||
}
|
||||
} else {
|
||||
// There's no specific file associated with the artifact, but
|
||||
// we can still tag the artifact itself
|
||||
actionsList.add(null);
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
|
||||
final Collection<BlackboardArtifact> selectedArtifactsList
|
||||
= new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
|
||||
if (selectedArtifactsList.size() == 1) {
|
||||
actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
|
||||
if (n != null) {
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
}
|
||||
|
||||
return actionsList;
|
||||
return Arrays.asList(ban.getActions(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -411,44 +252,8 @@ public class DataResultFilterNode extends FilterNode {
|
||||
|
||||
@Override
|
||||
protected List<Action> defaultVisit(DisplayableItemNode ditem) {
|
||||
//preserve the default node's actions
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
for (Action action : ditem.getActions(true)) {
|
||||
actions.add(action);
|
||||
}
|
||||
|
||||
return actions;
|
||||
return Arrays.asList(ditem.getActions(true));
|
||||
}
|
||||
|
||||
private Content findLinked(BlackboardArtifactNode ba) {
|
||||
BlackboardArtifact art = ba.getLookup().lookup(BlackboardArtifact.class);
|
||||
Content c = null;
|
||||
try {
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
|
||||
switch (attr.getAttributeType().getValueType()) {
|
||||
case INTEGER:
|
||||
int i = attr.getValueInt();
|
||||
if (i != -1) {
|
||||
c = art.getSleuthkitCase().getContentById(i);
|
||||
}
|
||||
break;
|
||||
case LONG:
|
||||
long l = attr.getValueLong();
|
||||
if (l != -1) {
|
||||
c = art.getSleuthkitCase().getContentById(l);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskException ex) {
|
||||
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error getting linked file", ex); //NON-NLS
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -488,12 +293,12 @@ public class DataResultFilterNode extends FilterNode {
|
||||
|
||||
@Override
|
||||
public AbstractAction visit(BlackboardArtifactNode ban) {
|
||||
|
||||
|
||||
Action preferredAction = ban.getPreferredAction();
|
||||
if(preferredAction instanceof AbstractAction) {
|
||||
if (preferredAction instanceof AbstractAction) {
|
||||
return (AbstractAction) preferredAction;
|
||||
}
|
||||
|
||||
|
||||
BlackboardArtifact artifact = ban.getArtifact();
|
||||
try {
|
||||
if ((artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())
|
||||
@ -567,7 +372,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
// is a DirectoryTreeFilterNode that wraps the dataModelNode. We need
|
||||
// to set that wrapped node as the selection and root context of the
|
||||
// directory tree explorer manager (sourceEm)
|
||||
if(sourceEm == null || sourceEm.getSelectedNodes().length == 0) {
|
||||
if (sourceEm == null || sourceEm.getSelectedNodes().length == 0) {
|
||||
return null;
|
||||
}
|
||||
final Node currentSelectionInDirectoryTree = sourceEm.getSelectedNodes()[0];
|
||||
@ -610,7 +415,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
* @return
|
||||
*/
|
||||
private AbstractAction openParent(AbstractNode node) {
|
||||
if(sourceEm == null) {
|
||||
if (sourceEm == null) {
|
||||
return null;
|
||||
}
|
||||
// @@@ Why do we ignore node?
|
||||
|
@ -28,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -83,6 +84,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits;
|
||||
import org.sleuthkit.autopsy.datamodel.KeywordHits;
|
||||
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
|
||||
import org.sleuthkit.autopsy.datamodel.DataArtifacts;
|
||||
import org.sleuthkit.autopsy.datamodel.OsAccounts;
|
||||
import org.sleuthkit.autopsy.datamodel.PersonNode;
|
||||
import org.sleuthkit.autopsy.datamodel.Tags;
|
||||
import org.sleuthkit.autopsy.datamodel.ViewsNode;
|
||||
@ -95,6 +97,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
@ -1220,6 +1223,85 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does depth-first search to find os account list node where the provided
|
||||
* os account is a child.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param osAccount The os account.
|
||||
*
|
||||
* @return The parent list node of the os account if found or empty if not.
|
||||
*/
|
||||
private Optional<Node> getOsAccountListNode(Node node, OsAccount osAccount, Set<Host> hosts) {
|
||||
if (node == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Host nodeHost = node.getLookup().lookup(Host.class);
|
||||
if ((nodeHost != null && hosts != null && hosts.contains(nodeHost))
|
||||
|| node.getLookup().lookup(DataSource.class) != null
|
||||
|| node.getLookup().lookup(Person.class) != null
|
||||
|| PersonNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
|
||||
|
||||
return Stream.of(node.getChildren().getNodes(true))
|
||||
.map(childNode -> getOsAccountListNode(childNode, osAccount, hosts))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.findFirst();
|
||||
|
||||
}
|
||||
|
||||
if (OsAccounts.getListName().equals(node.getName())) {
|
||||
return Optional.of(node);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the os account if the os account is found in the tree.
|
||||
*
|
||||
* @param osAccount The os account.
|
||||
*/
|
||||
public void viewOsAccount(OsAccount osAccount) {
|
||||
Set<Host> hosts = null;
|
||||
|
||||
if (CasePreferences.getGroupItemsInTreeByDataSource()) {
|
||||
try {
|
||||
hosts = new HashSet<>(Case.getCurrentCase().getSleuthkitCase().getOsAccountManager().getHosts(osAccount));
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to get valid hosts for osAccount: " + osAccount, ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Host> finalHosts = hosts;
|
||||
|
||||
Optional<Node> osAccountListNodeOpt = Stream.of(em.getRootContext().getChildren().getNodes(true))
|
||||
.map(nd -> getOsAccountListNode(nd, osAccount, finalHosts))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.findFirst();
|
||||
|
||||
if (!osAccountListNodeOpt.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node osAccountListNode = osAccountListNodeOpt.get();
|
||||
|
||||
DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) osAccountListNode).getOriginal();
|
||||
undecoratedParentNode.setChildNodeSelectionInfo((osAcctNd) -> {
|
||||
OsAccount osAcctOfNd = osAcctNd.getLookup().lookup(OsAccount.class);
|
||||
return osAcctOfNd != null && osAcctOfNd.getId() == osAccount.getId();
|
||||
});
|
||||
getTree().expandNode(osAccountListNode);
|
||||
try {
|
||||
em.setExploredContextAndSelection(osAccountListNode, new Node[]{osAccountListNode});
|
||||
} catch (PropertyVetoException ex) {
|
||||
LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve the artifact type for the given artifact type id.
|
||||
*
|
||||
|
@ -126,6 +126,7 @@ public class DomainSearch {
|
||||
* @param caseDb The case database.
|
||||
* @param centralRepoDb The central repository database. Can be null
|
||||
* if not needed.
|
||||
* @param context The search context.
|
||||
*
|
||||
* @return A LinkedHashMap grouped and sorted according to the parameters.
|
||||
*
|
||||
|
@ -31,6 +31,7 @@ import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
@ -194,6 +195,7 @@ class SummaryHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"SummaryHelper.documentSummary.unable.to.read=Unable to extract text from file."})
|
||||
/**
|
||||
* Get the beginning of text from the specified AbstractFile.
|
||||
*
|
||||
@ -217,7 +219,7 @@ class SummaryHelpers {
|
||||
} catch (IOException ex) {
|
||||
return Bundle.FileSearch_documentSummary_noBytes();
|
||||
} catch (TextExtractor.InitReaderException ex) {
|
||||
return Bundle.FileSearch_documentSummary_noPreview();
|
||||
return Bundle.SummaryHelper_documentSummary_unable_to_read();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ ExportIngestHistory_startTimeColumn=Start Time
|
||||
ExportIngestHistory_versionColumn=Module Version
|
||||
ExportPastCases_caseColumn_title=Case
|
||||
ExportPastCases_countColumn_title=Count
|
||||
ExportPastCases_notableFileTable_tabName=Cases with Common Notable
|
||||
ExportPastCases_notableFileTable_tabName=Cases with Common Notable Items at Time Of Ingest
|
||||
ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices
|
||||
ExportRecentFiles_attachmentsTable_tabName=Recent Attachments
|
||||
ExportRecentFiles_col_head_date=Date
|
||||
|
@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
@Messages({
|
||||
"ExportPastCases_caseColumn_title=Case",
|
||||
"ExportPastCases_countColumn_title=Count",
|
||||
"ExportPastCases_notableFileTable_tabName=Cases with Common Notable",
|
||||
"ExportPastCases_notableFileTable_tabName=Cases with Common Notable Items at Time Of Ingest",
|
||||
"ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices",})
|
||||
class ExportPastCases {
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
OpenIDE-Module-Name=stixModule
|
||||
STIXReportModule.getName.text=STIX
|
||||
STIXReportModule.getDesc.text=Generate a report by running a collection of STIX (Structured Threat Information eXpression) files against the data sources. Also creates artifacts under Interesting Files.
|
||||
STIXReportModule.progress.readSTIX=Parsing STIX files
|
||||
STIXReportModuleConfigPanel.jLabel2.text=Select a STIX file or directory of STIX files
|
||||
STIXReportModuleConfigPanel.jButton1.text=Choose file
|
||||
STIXReportModuleConfigPanel.jCheckBox1.text=Include results for false indicators in output file
|
||||
STIXReportModule.notifyMsg.unableToOpenReportFile=Unable to complete STIX report.
|
||||
STIXReportModule.progress.completedWithErrors=Completed with errors
|
||||
STIXReportModule.notifyMsg.unableToOpenFileDir=Unable to open STIX file/directory {0}
|
||||
STIXReportModule.progress.couldNotOpenFileDir=Could not open file/directory {0}
|
||||
STIXReportModule.notifyMsg.tooManyArtifactsgt1000=Too many STIX-related artifacts generated for "{0}". Only saving first 1000.
|
||||
STIXReportModule.notifyErr.noFildDirProvided=No STIX file/directory provided
|
||||
STIXReportModule.progress.noFildDirProvided=No STIX file/directory provided
|
||||
STIXReportModuleConfigPanel.jStixFileTextField.text=
|
@ -1,18 +0,0 @@
|
||||
OpenIDE-Module-Name=stixModule
|
||||
StixArtifactData.indexError.message=Failed to index STIX interesting file hit artifact for keyword search.
|
||||
StixArtifactData.noOpenCase.errMsg=No open case available.
|
||||
STIXReportModule.getName.text=STIX
|
||||
STIXReportModule.getDesc.text=Generate a report by running a collection of STIX (Structured Threat Information eXpression) files against the data sources. Also creates artifacts under Interesting Files.
|
||||
STIXReportModule.progress.readSTIX=Parsing STIX files
|
||||
STIXReportModule.srcModuleName.text=STIX Report
|
||||
STIXReportModuleConfigPanel.jLabel2.text=Select a STIX file or directory of STIX files
|
||||
STIXReportModuleConfigPanel.jButton1.text=Choose file
|
||||
STIXReportModuleConfigPanel.jCheckBox1.text=Include results for false indicators in output file
|
||||
STIXReportModule.notifyMsg.unableToOpenReportFile=Unable to complete STIX report.
|
||||
STIXReportModule.progress.completedWithErrors=Completed with errors
|
||||
STIXReportModule.notifyMsg.unableToOpenFileDir=Unable to open STIX file/directory {0}
|
||||
STIXReportModule.progress.couldNotOpenFileDir=Could not open file/directory {0}
|
||||
STIXReportModule.notifyMsg.tooManyArtifactsgt1000=Too many STIX-related artifacts generated for "{0}". Only saving first 1000.
|
||||
STIXReportModule.notifyErr.noFildDirProvided=No STIX file/directory provided
|
||||
STIXReportModule.progress.noFildDirProvided=No STIX file/directory provided
|
||||
STIXReportModuleConfigPanel.jStixFileTextField.text=
|
@ -1,18 +0,0 @@
|
||||
#Mon Jul 12 13:22:00 UTC 2021
|
||||
OpenIDE-Module-Name=stix\u30e2\u30b8\u30e5\u30fc\u30eb
|
||||
STIXReportModule.getDesc.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u5bfe\u3057\u3066\u5e7e\u3064\u304b\u306eSTIX\uff08Structured Threat Information eXpression\uff1b\u8105\u5a01\u60c5\u5831\u69cb\u9020\u5316\u8a18\u8ff0\u5f62\u5f0f\uff09\u30d5\u30a1\u30a4\u30eb\u3092\u5b9f\u884c\u3057\u3001\u30ec\u30dd\u30fc\u30c8\u3092\u751f\u6210\u3057\u307e\u3059\u3002\u307e\u305f\u3001\u7591\u308f\u3057\u3044\u30d5\u30a1\u30a4\u30eb\u5185\u306b\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u4f5c\u6210\u3002
|
||||
STIXReportModule.getName.text=STIX
|
||||
STIXReportModule.notifyErr.noFildDirProvided=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093
|
||||
STIXReportModule.notifyMsg.tooManyArtifactsgt1000="{0}"\u7528\u306b\u751f\u6210\u3055\u308c\u305fSTIX\u95a2\u9023\u306e\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u304c\u591a\u3059\u304e\u307e\u3059\u3002\u6700\u521d\u306e1000\u306e\u307f\u4fdd\u5b58\u3055\u308c\u307e\u3059\u3002
|
||||
STIXReportModule.notifyMsg.unableToOpenFileDir=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea{0}\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f
|
||||
STIXReportModule.notifyMsg.unableToOpenReportFile=STIX\u30ec\u30dd\u30fc\u30c8\u3092\u5b8c\u4e86\u3067\u304d\u307e\u305b\u3093\u3002
|
||||
STIXReportModule.progress.completedWithErrors=\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u304c\u3001\u5b8c\u4e86\u3057\u307e\u3057\u305f
|
||||
STIXReportModule.progress.couldNotOpenFileDir=\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea{0}\u3092\u958b\u3051\u307e\u305b\u3093\u3067\u3057\u305f
|
||||
STIXReportModule.progress.noFildDirProvided=STIX\u30d5\u30a1\u30a4\u30eb\uff0f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093
|
||||
STIXReportModule.progress.readSTIX=STIX\u30d5\u30a1\u30a4\u30eb\u3092\u30d1\u30fc\u30b9\u4e2d
|
||||
STIXReportModule.srcModuleName.text=STIX\u30ec\u30dd\u30fc\u30c8
|
||||
STIXReportModuleConfigPanel.jButton1.text=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e
|
||||
STIXReportModuleConfigPanel.jCheckBox1.text=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u8aa4\u3063\u305f\u30a4\u30f3\u30b8\u30b1\u30fc\u30bf\u30fc\u306e\u7d50\u679c\u3082\u542b\u3080
|
||||
STIXReportModuleConfigPanel.jLabel2.text=STIX\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306fSTIX\u30d5\u30a1\u30a4\u30eb\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u9078\u629e
|
||||
StixArtifactData.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u7528\u306eSTIX\u8208\u5473\u6df1\u3044\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
|
||||
StixArtifactData.noOpenCase.errMsg=\u30aa\u30fc\u30d7\u30f3\u30b1\u30fc\u30b9\u306f\u3042\u308a\u307e\u305b\u3093\u3002
|
@ -1,227 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.mitre.cybox.objects.AccountObjectType;
|
||||
import org.mitre.cybox.objects.UserAccountObjectType;
|
||||
import org.mitre.cybox.objects.WindowsUserAccount;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalAccountObj extends EvaluatableObject {
|
||||
|
||||
private final AccountObjectType obj;
|
||||
|
||||
EvalAccountObj(AccountObjectType a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
@SuppressWarnings( "deprecation" )
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
// Fields we can search for:
|
||||
// UserAccount: Home_Directory, Username
|
||||
// WinUserAccount: SID
|
||||
if (!(obj instanceof UserAccountObjectType)) {
|
||||
return new ObservableResult(id, "AccountObject: Can not process \"Account\" - need a User_Account or Windows_User_Account", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// For displaying what we were looking for in the results
|
||||
String searchString = "";
|
||||
|
||||
// Check which fields are present and record them
|
||||
boolean haveHomeDir = false;
|
||||
boolean haveUsername = false;
|
||||
boolean haveSID = false;
|
||||
|
||||
UserAccountObjectType userAccountObj = (UserAccountObjectType) obj;
|
||||
if (userAccountObj.getHomeDirectory() != null) {
|
||||
haveHomeDir = true;
|
||||
searchString = "HomeDir \"" + userAccountObj.getHomeDirectory().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getUsername() != null) {
|
||||
haveUsername = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Username \"" + userAccountObj.getUsername().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
WindowsUserAccount winUserObj = null;
|
||||
if (obj instanceof WindowsUserAccount) {
|
||||
winUserObj = (WindowsUserAccount) obj;
|
||||
|
||||
if (winUserObj.getSecurityID() != null) {
|
||||
haveSID = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "SID \"" + winUserObj.getSecurityID().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
if (!(haveHomeDir || haveUsername || haveSID)) {
|
||||
return new ObservableResult(id, "AccountObject: No evaluatable fields found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// Set warnings for any unsupported fields
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
// The assumption here is that there aren't going to be too many network shares, so we
|
||||
// can cycle through all of them.
|
||||
try {
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<>();
|
||||
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
boolean foundHomeDirMatch = false;
|
||||
boolean foundUsernameMatch = false;
|
||||
boolean foundSIDMatch = false;
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID())
|
||||
&& (haveHomeDir)) {
|
||||
foundHomeDirMatch = compareStringObject(userAccountObj.getHomeDirectory(), attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME.getTypeID())
|
||||
&& (haveUsername)) {
|
||||
foundUsernameMatch = compareStringObject(userAccountObj.getUsername(), attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID.getTypeID())
|
||||
&& (haveSID) && (winUserObj != null)) {
|
||||
foundSIDMatch = compareStringObject(winUserObj.getSecurityID(), attr.getValueString());
|
||||
}
|
||||
}
|
||||
|
||||
if (((!haveHomeDir) || foundHomeDirMatch)
|
||||
&& ((!haveUsername) || foundUsernameMatch)
|
||||
&& ((!haveSID) || foundSIDMatch)) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if we found any matches
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "Account")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "AccountObject: Found a match for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Didn't find any matches
|
||||
return new ObservableResult(id, "AccountObject: No matches found for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "AccountObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the warning for any fields in the object that aren't supported.
|
||||
*/
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
|
||||
if (obj.getDescription() != null) {
|
||||
fieldNames.add("Description"); //NON-NLS
|
||||
}
|
||||
if (obj.getDomain() != null) {
|
||||
fieldNames.add("Domain"); //NON-NLS
|
||||
}
|
||||
if (obj.getAuthentications() != null) {
|
||||
fieldNames.add("Authentication"); //NON-NLS
|
||||
}
|
||||
if (obj.getCreationDate() != null) {
|
||||
fieldNames.add("Creation_Date"); //NON-NLS
|
||||
}
|
||||
if (obj.getModifiedDate() != null) {
|
||||
fieldNames.add("Modified_Date"); //NON-NLS
|
||||
}
|
||||
if (obj.getLastAccessedTime() != null) {
|
||||
fieldNames.add("Last_Accessed_Time"); //NON-NLS
|
||||
}
|
||||
|
||||
if (obj instanceof UserAccountObjectType) {
|
||||
UserAccountObjectType userAccountObj = (UserAccountObjectType) obj;
|
||||
if (userAccountObj.getFullName() != null) {
|
||||
fieldNames.add("Full_Name"); //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getGroupList() != null) {
|
||||
fieldNames.add("Group_List"); //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getLastLogin() != null) {
|
||||
fieldNames.add("Last_Login"); //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getPrivilegeList() != null) {
|
||||
fieldNames.add("Privilege_List"); //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getScriptPath() != null) {
|
||||
fieldNames.add("Script_Path"); //NON-NLS
|
||||
}
|
||||
if (userAccountObj.getUserPasswordAge() != null) {
|
||||
fieldNames.add("User_Password_Age"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
if (obj instanceof WindowsUserAccount) {
|
||||
WindowsUserAccount winUserObj = (WindowsUserAccount) obj;
|
||||
|
||||
if (winUserObj.getSecurityType() != null) {
|
||||
fieldNames.add("Security_Type"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
|
||||
import org.mitre.cybox.objects.Address;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalAddressObj extends EvaluatableObject {
|
||||
|
||||
private final Address obj;
|
||||
|
||||
public EvalAddressObj(Address a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
if (obj.getAddressValue() == null) {
|
||||
return new ObservableResult(id, "AddressObject: No address value field found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
Case case1;
|
||||
try {
|
||||
case1 = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "Exception while getting open case.", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
|
||||
String origAddressStr = obj.getAddressValue().getValue().toString();
|
||||
|
||||
// For now, we don't support "NONE" because it honestly doesn't seem like it
|
||||
// would ever appear in practice.
|
||||
if (((obj.getAddressValue().getApplyCondition() != null)
|
||||
&& (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.NONE))) {
|
||||
return new ObservableResult(id, "AddressObject: Can not process apply condition " + obj.getAddressValue().getApplyCondition().toString() //NON-NLS
|
||||
+ " on Address object", spacing, ObservableResult.ObservableState.INDETERMINATE, null); //NON-NLS
|
||||
}
|
||||
|
||||
// Set warnings for any unsupported fields
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
|
||||
try {
|
||||
// Need to check that every part of the string had at least one match
|
||||
// in the AND case
|
||||
boolean everyPartMatched = true;
|
||||
List<BlackboardArtifact> combinedArts = new ArrayList<BlackboardArtifact>();
|
||||
String searchString = "";
|
||||
String[] parts = origAddressStr.split("##comma##"); //NON-NLS
|
||||
|
||||
for (String addressStr : parts) {
|
||||
|
||||
// Update the string to show in the results
|
||||
if (!searchString.isEmpty()) {
|
||||
|
||||
if ((obj.getAddressValue().getApplyCondition() != null)
|
||||
&& (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.ALL)) {
|
||||
searchString += " AND "; //NON-NLS
|
||||
} else {
|
||||
searchString += " OR "; //NON-NLS
|
||||
}
|
||||
}
|
||||
searchString += addressStr;
|
||||
|
||||
if ((obj.getAddressValue().getCondition() == null)
|
||||
|| (obj.getAddressValue().getCondition() == ConditionTypeEnum.EQUALS)) {
|
||||
List<BlackboardArtifact> arts = sleuthkitCase.getBlackboardArtifacts(
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD,
|
||||
addressStr);
|
||||
|
||||
if (arts.isEmpty()) {
|
||||
everyPartMatched = false;
|
||||
} else {
|
||||
combinedArts.addAll(arts);
|
||||
}
|
||||
|
||||
} else {
|
||||
// This is inefficient, but the easiest way to do it.
|
||||
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
// Get all the URL artifacts
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
|
||||
if (compareStringObject(addressStr, obj.getAddressValue().getCondition(),
|
||||
obj.getAddressValue().getApplyCondition(), attr.getValueString())) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (finalHits.isEmpty()) {
|
||||
everyPartMatched = false;
|
||||
} else {
|
||||
combinedArts.addAll(finalHits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're in the ALL case, make sure every piece matched
|
||||
if ((obj.getAddressValue().getApplyCondition() != null)
|
||||
&& (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.ALL)
|
||||
&& (!everyPartMatched)) {
|
||||
return new ObservableResult(id, "AddressObject: No matches for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
|
||||
if (!combinedArts.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : combinedArts) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "AddressObject")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "AddressObject: Found a match for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "AddressObject: Found no matches for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
return new ObservableResult(id, "AddressObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the warning for any fields in the object that aren't supported.
|
||||
*/
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
|
||||
if (obj.getVLANName() != null) {
|
||||
fieldNames.add("VLAN_Name"); //NON-NLS
|
||||
}
|
||||
if (obj.getVLANName() != null) {
|
||||
fieldNames.add("VLAN_Num"); //NON-NLS
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
import org.mitre.cybox.objects.DomainName;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalDomainObj extends EvaluatableObject {
|
||||
|
||||
private final DomainName obj;
|
||||
|
||||
public EvalDomainObj(DomainName a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
if (obj.getValue() == null) {
|
||||
return new ObservableResult(id, "DomainObject: No domain value field found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
Case case1;
|
||||
try {
|
||||
case1 = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "Exception while getting open case.", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
// Since we have single URL artifacts, ALL and NONE conditions probably don't make sense to test
|
||||
if (!((obj.getValue().getApplyCondition() == null)
|
||||
|| (obj.getValue().getApplyCondition() == ConditionApplicationEnum.ANY))) {
|
||||
return new ObservableResult(id, "DomainObject: Can not process apply condition " + obj.getValue().getApplyCondition().toString() //NON-NLS
|
||||
+ " on Domain object", spacing, ObservableResult.ObservableState.INDETERMINATE, null); //NON-NLS
|
||||
}
|
||||
|
||||
// If the condition is not "CONTAINS", add a warning that it's being ignored
|
||||
if ((obj.getValue().getCondition() != null)
|
||||
&& (obj.getValue().getCondition() != ConditionTypeEnum.CONTAINS)) {
|
||||
addWarning("Warning: Ignoring condition " + obj.getValue().getCondition().toString() //NON-NLS
|
||||
+ " on DomainName - using substring comparison"); //NON-NLS
|
||||
}
|
||||
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
|
||||
try {
|
||||
// Set up the list of matching artifacts
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
// Get all the URL artifacts
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
|
||||
String url = attr.getValueString();
|
||||
|
||||
// Check whether the domain name is a substring of the URL (regardless
|
||||
// of the condition on the domain name object)
|
||||
if (compareStringObject(obj.getValue().getValue().toString(), ConditionTypeEnum.CONTAINS,
|
||||
obj.getValue().getApplyCondition(), url)) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "DomainNameObject")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "DomainNameObject: Found a match for " + obj.getValue().getValue().toString() //NON-NLS
|
||||
+ " " + getPrintableWarnings(),
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "DomainNameObject: Found no matches for " + obj.getValue().getValue().toString() //NON-NLS
|
||||
+ " " + getPrintableWarnings(),
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} catch (TskCoreException ex) {
|
||||
return new ObservableResult(id, "DomainNameObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.mitre.cybox.objects.EmailMessage;
|
||||
import org.mitre.cybox.objects.Address;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalEmailObj extends EvaluatableObject {
|
||||
|
||||
private final EmailMessage obj;
|
||||
|
||||
private List<BlackboardArtifact> finalHits;
|
||||
|
||||
public EvalEmailObj(EmailMessage a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
|
||||
finalHits = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
List<BlackboardArtifact> toHits = null;
|
||||
boolean hadToFields = false;
|
||||
List<BlackboardArtifact> ccHits = null;
|
||||
boolean hadCcFields = false;
|
||||
List<BlackboardArtifact> fromHits = null;
|
||||
boolean hadFromField = false;
|
||||
List<BlackboardArtifact> subjectHits = null;
|
||||
boolean hadSubjectField = false;
|
||||
|
||||
if (obj.getHeader() != null) {
|
||||
if ((obj.getHeader().getTo() != null)
|
||||
&& (obj.getHeader().getTo().getRecipients() != null)
|
||||
&& (!obj.getHeader().getTo().getRecipients().isEmpty())) {
|
||||
for (Address addr : obj.getHeader().getTo().getRecipients()) {
|
||||
if (addr.getAddressValue() != null) {
|
||||
|
||||
hadToFields = true;
|
||||
|
||||
try {
|
||||
toHits = findArtifactsBySubstring(addr.getAddressValue(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((obj.getHeader().getCC() != null)
|
||||
&& (obj.getHeader().getCC().getRecipients() != null)
|
||||
&& (!obj.getHeader().getCC().getRecipients().isEmpty())) {
|
||||
for (Address addr : obj.getHeader().getCC().getRecipients()) {
|
||||
if (addr.getAddressValue() != null) {
|
||||
|
||||
hadCcFields = true;
|
||||
|
||||
try {
|
||||
ccHits = findArtifactsBySubstring(addr.getAddressValue(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CC);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((obj.getHeader().getFrom() != null)
|
||||
&& (obj.getHeader().getFrom().getAddressValue() != null)) {
|
||||
|
||||
hadFromField = true;
|
||||
|
||||
try {
|
||||
fromHits = findArtifactsBySubstring(obj.getHeader().getFrom().getAddressValue(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((obj.getHeader().getSubject() != null)
|
||||
&& (obj.getHeader().getSubject().getValue() != null)) {
|
||||
|
||||
hadSubjectField = true;
|
||||
|
||||
try {
|
||||
subjectHits = findArtifactsBySubstring(obj.getHeader().getSubject(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure at least one test had some data
|
||||
if ((!hadToFields) && (!hadFromField) && (!hadCcFields) && (!hadSubjectField)) {
|
||||
return new ObservableResult(id, "EmailMessage: Could not find any parsable EmailMessage fields " //NON-NLS
|
||||
+ getPrintableWarnings(),
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// Check if there were more fields that aren't currently supported
|
||||
String fieldNames = getListOfUnsupportedFields();
|
||||
if (fieldNames.length() > 0) {
|
||||
addWarning("Unsupported field(s) found: " + fieldNames); //NON-NLS
|
||||
}
|
||||
|
||||
// Find the artifacts that matched all of the fields
|
||||
finalHits = null;
|
||||
boolean finalHitsStarted = false;
|
||||
|
||||
if (hadToFields) {
|
||||
combineHits(toHits, finalHitsStarted);
|
||||
finalHitsStarted = true;
|
||||
}
|
||||
if (hadCcFields) {
|
||||
combineHits(ccHits, finalHitsStarted);
|
||||
finalHitsStarted = true;
|
||||
}
|
||||
if (hadFromField) {
|
||||
combineHits(fromHits, finalHitsStarted);
|
||||
finalHitsStarted = true;
|
||||
}
|
||||
if (hadSubjectField) {
|
||||
combineHits(subjectHits, finalHitsStarted);
|
||||
finalHitsStarted = true;
|
||||
}
|
||||
|
||||
if (!finalHitsStarted) {
|
||||
// We didn't find any fields that could be evaluated
|
||||
return new ObservableResult(id, "EmailMessage: EmailObj parsing incomplete " + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// If there are any artifacts left in finalHits, we have a match
|
||||
if (finalHits.size() > 0) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "EmailMessage")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "EmailMessage: " + finalHits.size() + " matching artifacts found " + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
} else {
|
||||
return new ObservableResult(id, "EmailMessage: No matching artifacts found " + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a set of hits to the final set of hits. Removes any artifacts that
|
||||
* aren't found in the new set. The final list is the artifacts found in all
|
||||
* sets.
|
||||
*
|
||||
* @param newHits The new hits to add to the list
|
||||
* @param finalHitsStarted Whether we've started the list or not
|
||||
*/
|
||||
private void combineHits(List<BlackboardArtifact> newHits, boolean finalHitsStarted) {
|
||||
if (finalHitsStarted && (finalHits != null)) {
|
||||
finalHits.retainAll(newHits);
|
||||
} else {
|
||||
finalHits = newHits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the Email Object has any fields set that we don't support
|
||||
* right now.
|
||||
*
|
||||
* @return a list of unsupported fields found.
|
||||
*/
|
||||
private String getListOfUnsupportedFields() {
|
||||
String fieldNames = "";
|
||||
if (obj.getHeader() != null) {
|
||||
if (obj.getHeader().getReceivedLines() != null) {
|
||||
fieldNames += "Received_Lines "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getBCC() != null) {
|
||||
fieldNames += "BCC "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getInReplyTo() != null) {
|
||||
fieldNames += "In_Reply_To "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getDate() != null) {
|
||||
fieldNames += "Date "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getMessageID() != null) {
|
||||
fieldNames += "Message_ID "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getSender() != null) {
|
||||
fieldNames += "Sender "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getReplyTo() != null) {
|
||||
fieldNames += "Reply_To "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getErrorsTo() != null) {
|
||||
fieldNames += "Errors_To "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getBoundary() != null) {
|
||||
fieldNames += "Boundary "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getContentType() != null) {
|
||||
fieldNames += "Content_Type "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getMIMEVersion() != null) {
|
||||
fieldNames += "MIME_Version "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getPrecedence() != null) {
|
||||
fieldNames += "Precedence "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getUserAgent() != null) {
|
||||
fieldNames += "User_Agent "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getXMailer() != null) {
|
||||
fieldNames += "X_Mailer "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getXOriginatingIP() != null) {
|
||||
fieldNames += "X_Originiating_IP "; //NON-NLS
|
||||
}
|
||||
if (obj.getHeader().getXPriority() != null) {
|
||||
fieldNames += "X_Priority "; //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
if (obj.getEmailServer() != null) {
|
||||
fieldNames += "Email_Server "; //NON-NLS
|
||||
}
|
||||
if (obj.getRawBody() != null) {
|
||||
fieldNames += "Raw_Body "; //NON-NLS
|
||||
}
|
||||
if (obj.getRawHeader() != null) {
|
||||
fieldNames += "Raw_Header "; //NON-NLS
|
||||
}
|
||||
if (obj.getAttachments() != null) {
|
||||
fieldNames += "Attachments "; //NON-NLS
|
||||
}
|
||||
if (obj.getLinks() != null) {
|
||||
fieldNames += "Links "; //NON-NLS
|
||||
}
|
||||
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
}
|
@ -1,699 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
|
||||
import org.mitre.cybox.objects.FileObjectType;
|
||||
import org.mitre.cybox.objects.WindowsExecutableFileObjectType;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
import org.mitre.cybox.common_2.DatatypeEnum;
|
||||
import org.mitre.cybox.common_2.HashType;
|
||||
import org.mitre.cybox.common_2.DateTimeObjectPropertyType;
|
||||
import org.mitre.cybox.common_2.StringObjectPropertyType;
|
||||
import org.mitre.cybox.common_2.UnsignedLongObjectPropertyType;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalFileObj extends EvaluatableObject {
|
||||
|
||||
private final FileObjectType obj;
|
||||
|
||||
public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
Case case1;
|
||||
try {
|
||||
case1 = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "Exception while getting open case.", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
|
||||
setWarnings("");
|
||||
String whereClause = "";
|
||||
|
||||
if (obj.getSizeInBytes() != null) {
|
||||
try {
|
||||
String newClause = processULongObject(obj.getSizeInBytes(), "size"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getFileName() != null) {
|
||||
try {
|
||||
String newClause = processStringObject(obj.getFileName(), "name"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getFileExtension() != null) {
|
||||
if ((obj.getFileExtension().getCondition() == null)
|
||||
|| (obj.getFileExtension().getCondition() == ConditionTypeEnum.EQUALS)) {
|
||||
String newClause = "LOWER(name) LIKE LOWER(\'%" + obj.getFileExtension().getValue() + "\')"; //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} else {
|
||||
addWarning(
|
||||
"Could not process condition " + obj.getFileExtension().getCondition().value() + " on file extension"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getFilePath() != null) {
|
||||
try {
|
||||
|
||||
String[] parts = obj.getFilePath().getValue().toString().split("##comma##"); //NON-NLS
|
||||
String finalPathStr = "";
|
||||
|
||||
for (String filePath : parts) {
|
||||
// First, we need to normalize the path
|
||||
String currentFilePath = filePath;
|
||||
|
||||
// Remove the drive letter
|
||||
if (currentFilePath.matches("^[A-Za-z]:.*")) {
|
||||
currentFilePath = currentFilePath.substring(2);
|
||||
}
|
||||
|
||||
// Change any backslashes to forward slashes
|
||||
currentFilePath = currentFilePath.replace("\\", "/");
|
||||
|
||||
// The path needs to start with a slash
|
||||
if (!currentFilePath.startsWith("/")) {
|
||||
currentFilePath = "/" + currentFilePath;
|
||||
}
|
||||
|
||||
// If the path does not end in a slash, the final part should be the file name.
|
||||
if (!currentFilePath.endsWith("/")) {
|
||||
int lastSlash = currentFilePath.lastIndexOf('/');
|
||||
if (lastSlash >= 0) {
|
||||
currentFilePath = currentFilePath.substring(0, lastSlash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct the path string (which may be multi-part)
|
||||
if (!finalPathStr.isEmpty()) {
|
||||
finalPathStr += "##comma##"; //NON-NLS
|
||||
}
|
||||
finalPathStr += currentFilePath;
|
||||
}
|
||||
|
||||
String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
|
||||
obj.getFilePath().getApplyCondition(), "parent_path"); //NON-NLS
|
||||
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getCreatedTime() != null) {
|
||||
try {
|
||||
String newClause = processTimestampObject(obj.getCreatedTime(), "crtime"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getModifiedTime() != null) {
|
||||
try {
|
||||
String newClause = processTimestampObject(obj.getModifiedTime(), "mtime"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getAccessedTime() != null) {
|
||||
try {
|
||||
String newClause = processTimestampObject(obj.getAccessedTime(), "atime"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getHashes() != null) {
|
||||
for (HashType h : obj.getHashes().getHashes()) {
|
||||
if (h.getSimpleHashValue() != null) {
|
||||
if (h.getType().getValue().equals("MD5")) { //NON-NLS
|
||||
String newClause = "";
|
||||
if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains("##comma##")) { //NON-NLS
|
||||
String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split("##comma##"); //NON-NLS
|
||||
String hashList = "";
|
||||
for (String s : parts) {
|
||||
if (!hashList.isEmpty()) {
|
||||
hashList += ", ";
|
||||
}
|
||||
hashList += "\'" + s + "\'";
|
||||
}
|
||||
newClause = "md5 IN (" + hashList + ")"; //NON-NLS
|
||||
} else {
|
||||
newClause = "md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() + "\'"; //NON-NLS
|
||||
}
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} else {
|
||||
addWarning("Could not process hash type " + h.getType().getValue().toString()); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
addWarning("Could not process non-simple hash value"); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj instanceof WindowsExecutableFileObjectType) {
|
||||
WindowsExecutableFileObjectType winExe = (WindowsExecutableFileObjectType) obj;
|
||||
if (winExe.getHeaders() != null) {
|
||||
if (winExe.getHeaders().getFileHeader() != null) {
|
||||
if (winExe.getHeaders().getFileHeader().getTimeDateStamp() != null) {
|
||||
try {
|
||||
String result = convertTimestampString(winExe.getHeaders().getFileHeader().getTimeDateStamp().getValue().toString());
|
||||
String newClause = processNumericFields(result,
|
||||
winExe.getHeaders().getFileHeader().getTimeDateStamp().getCondition(),
|
||||
winExe.getHeaders().getFileHeader().getTimeDateStamp().getApplyCondition(),
|
||||
"crtime"); //NON-NLS
|
||||
whereClause = addClause(whereClause, newClause);
|
||||
} catch (TskCoreException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String unsupportedFields = listUnsupportedFields();
|
||||
if (!unsupportedFields.isEmpty()) {
|
||||
addWarning("Unsupported fields: " + unsupportedFields); //NON-NLS
|
||||
}
|
||||
|
||||
if (whereClause.length() > 0) {
|
||||
try {
|
||||
List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
|
||||
|
||||
if (!matchingFiles.isEmpty()) {
|
||||
|
||||
if (listSecondaryFields().isEmpty()) {
|
||||
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (AbstractFile a : matchingFiles) {
|
||||
artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
} else {
|
||||
|
||||
// We need to tag the matching files in Autopsy, so keep track of them
|
||||
List<AbstractFile> secondaryHits = new ArrayList<AbstractFile>();
|
||||
|
||||
for (AbstractFile file : matchingFiles) {
|
||||
boolean passedTests = true;
|
||||
|
||||
if (obj.isIsMasqueraded() != null) {
|
||||
List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED);
|
||||
boolean isMasq = false;
|
||||
if (!arts.isEmpty()) {
|
||||
isMasq = true;
|
||||
}
|
||||
|
||||
if (obj.isIsMasqueraded() != isMasq) {
|
||||
passedTests = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (obj.getFileFormat() != null) {
|
||||
|
||||
String formatsFound = file.getMIMEType();
|
||||
if (formatsFound != null) {
|
||||
if (!(formatsFound.equalsIgnoreCase(obj.getFileFormat().getValue().toString()))) {
|
||||
addWarning("Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() //NON-NLS
|
||||
+ " against " + formatsFound); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
addWarning("Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() //NON-NLS
|
||||
+ " (no file formats found)"); //NON-NLS
|
||||
}
|
||||
// It looks like the STIX file formats can be different than what Autopsy stores
|
||||
// (mime vs. unix file), so don't kill a file based on this field not matching.
|
||||
//if (!foundMatch) {
|
||||
// passedTests = false;
|
||||
//}
|
||||
}
|
||||
if (passedTests) {
|
||||
secondaryHits.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (secondaryHits.isEmpty()) {
|
||||
|
||||
return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause //NON-NLS
|
||||
+ " but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} else {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (AbstractFile a : secondaryHits) {
|
||||
artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "FileObject: Found " + secondaryHits.size() + " matches for " + whereClause //NON-NLS
|
||||
+ " and secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return new ObservableResult(id, "FileObject: Found no matches for " + whereClause + getPrintableWarnings(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
return new ObservableResult(id, "FileObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "FileObject: No evaluatable fields " + getPrintableWarnings(), spacing, //NON-NLS
|
||||
ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of secondary fields. These are the ones that we only test
|
||||
* on the matches for the primary fields.
|
||||
*
|
||||
* @return List of secondary fields
|
||||
*/
|
||||
private String listSecondaryFields() {
|
||||
String secondaryFields = "";
|
||||
|
||||
if (obj.isIsMasqueraded() != null) {
|
||||
secondaryFields += "is_masqueraded "; //NON-NLS
|
||||
}
|
||||
|
||||
if (obj.getFileFormat() != null) {
|
||||
secondaryFields += "File_Format "; //NON-NLS
|
||||
}
|
||||
|
||||
return secondaryFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* List unsupported fields found in the object.
|
||||
*
|
||||
* @return List of unsupported fields
|
||||
*/
|
||||
private String listUnsupportedFields() {
|
||||
String unsupportedFields = "";
|
||||
|
||||
if (obj.isIsPacked() != null) {
|
||||
unsupportedFields += "is_packed "; //NON-NLS
|
||||
}
|
||||
if (obj.getDevicePath() != null) {
|
||||
unsupportedFields += "Device_Path "; //NON-NLS
|
||||
}
|
||||
if (obj.getFullPath() != null) {
|
||||
unsupportedFields += "Full_Path "; //NON-NLS
|
||||
}
|
||||
if (obj.getMagicNumber() != null) {
|
||||
unsupportedFields += "Magic_Number "; //NON-NLS
|
||||
}
|
||||
if (obj.getDigitalSignatures() != null) {
|
||||
unsupportedFields += "Digital_Signatures "; //NON-NLS
|
||||
}
|
||||
if (obj.getFileAttributesList() != null) {
|
||||
unsupportedFields += "File_Attributes_List "; //NON-NLS
|
||||
}
|
||||
if (obj.getPermissions() != null) {
|
||||
unsupportedFields += "Permissions "; //NON-NLS
|
||||
}
|
||||
if (obj.getUserOwner() != null) {
|
||||
unsupportedFields += "User_Owner "; //NON-NLS
|
||||
}
|
||||
if (obj.getPackerList() != null) {
|
||||
unsupportedFields += "Packer_List "; //NON-NLS
|
||||
}
|
||||
if (obj.getPeakEntropy() != null) {
|
||||
unsupportedFields += "Peak_Entropy "; //NON-NLS
|
||||
}
|
||||
if (obj.getSymLinks() != null) {
|
||||
unsupportedFields += "Sym_Links "; //NON-NLS
|
||||
}
|
||||
if (obj.getByteRuns() != null) {
|
||||
unsupportedFields += "Bytes_Runs "; //NON-NLS
|
||||
}
|
||||
if (obj.getExtractedFeatures() != null) {
|
||||
unsupportedFields += "Extracted_Features "; //NON-NLS
|
||||
}
|
||||
if (obj.getEncryptionAlgorithm() != null) {
|
||||
unsupportedFields += "Encryption_Algorithm "; //NON-NLS
|
||||
}
|
||||
if (obj.getDecryptionKey() != null) {
|
||||
unsupportedFields += "Decryption_Key "; //NON-NLS
|
||||
}
|
||||
if (obj.getCompressionMethod() != null) {
|
||||
unsupportedFields += "Compression_Method "; //NON-NLS
|
||||
}
|
||||
if (obj.getCompressionVersion() != null) {
|
||||
unsupportedFields += "Compression_Version "; //NON-NLS
|
||||
}
|
||||
if (obj.getCompressionComment() != null) {
|
||||
unsupportedFields += "Compression_Comment "; //NON-NLS
|
||||
}
|
||||
|
||||
return unsupportedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert timestamp string into a long.
|
||||
*
|
||||
* @param timeStr
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
private static long convertTimestamp(String timeStr) throws ParseException {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //NON-NLS
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); //NON-NLS
|
||||
Date parsedDate = dateFormat.parse(timeStr);
|
||||
|
||||
Long unixTime = parsedDate.getTime() / 1000;
|
||||
|
||||
return unixTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL clause for an unsigned long object. Splits into fields and
|
||||
* call the more generic version of the function.
|
||||
*
|
||||
* @param longObj The Cybox UnsignedLong object
|
||||
* @param fieldName Name of the field to test against
|
||||
*
|
||||
* @return SQL clause
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
|
||||
throws TskCoreException {
|
||||
|
||||
return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
|
||||
longObj.getApplyCondition(), fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL clause for a numeric object.
|
||||
*
|
||||
* @param valueStr Value (as string)
|
||||
* @param typeCondition Cybox condition
|
||||
* @param applyCondition Cybox apply_condition
|
||||
* @param fieldName Name of the field to test against
|
||||
*
|
||||
* @return SQL clause
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
|
||||
ConditionApplicationEnum applyCondition, String fieldName)
|
||||
throws TskCoreException {
|
||||
|
||||
if ((typeCondition == null)
|
||||
|| ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
|
||||
&& (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
|
||||
|
||||
String fullClause = "";
|
||||
|
||||
if (valueStr.isEmpty()) {
|
||||
throw new TskCoreException("Empty value field"); //NON-NLS
|
||||
}
|
||||
|
||||
String[] parts = valueStr.split("##comma##"); //NON-NLS
|
||||
|
||||
for (String valuePart : parts) {
|
||||
String partialClause;
|
||||
|
||||
if ((typeCondition == null)
|
||||
|| (typeCondition == ConditionTypeEnum.EQUALS)) {
|
||||
|
||||
partialClause = fieldName + "=" + valuePart;
|
||||
} else if (typeCondition == ConditionTypeEnum.DOES_NOT_EQUAL) {
|
||||
partialClause = fieldName + "!=" + valuePart;
|
||||
} else if (typeCondition == ConditionTypeEnum.GREATER_THAN) {
|
||||
partialClause = fieldName + ">" + valuePart;
|
||||
} else if (typeCondition == ConditionTypeEnum.GREATER_THAN_OR_EQUAL) {
|
||||
partialClause = fieldName + ">=" + valuePart;
|
||||
} else if (typeCondition == ConditionTypeEnum.LESS_THAN) {
|
||||
partialClause = fieldName + "<" + valuePart;
|
||||
} else if (typeCondition == ConditionTypeEnum.LESS_THAN_OR_EQUAL) {
|
||||
partialClause = fieldName + "<=" + valuePart;
|
||||
} else {
|
||||
throw new TskCoreException("Could not process condition " + typeCondition.value() + " on " + fieldName); //NON-NLS
|
||||
}
|
||||
|
||||
if (fullClause.isEmpty()) {
|
||||
|
||||
if (parts.length > 1) {
|
||||
fullClause += "( ";
|
||||
}
|
||||
if (applyCondition == ConditionApplicationEnum.NONE) {
|
||||
fullClause += " NOT "; //NON-NLS
|
||||
}
|
||||
fullClause += partialClause;
|
||||
} else {
|
||||
if (applyCondition == ConditionApplicationEnum.ALL) {
|
||||
fullClause += " AND " + partialClause; //NON-NLS
|
||||
} else if (applyCondition == ConditionApplicationEnum.NONE) {
|
||||
fullClause += " AND NOT " + partialClause; //NON-NLS
|
||||
} else {
|
||||
fullClause += " OR " + partialClause; //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length > 1) {
|
||||
fullClause += " )";
|
||||
}
|
||||
|
||||
return fullClause;
|
||||
} else {
|
||||
// I don't think apply conditions make sense for these two.
|
||||
if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
|
||||
String[] parts = valueStr.split("##comma##"); //NON-NLS
|
||||
if (parts.length != 2) {
|
||||
throw new TskCoreException("Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName //NON-NLS
|
||||
+ "(" + valueStr + ")");
|
||||
}
|
||||
return (fieldName + ">=" + parts[0] + " AND " + fieldName + "<=" + parts[1]); //NON-NLS
|
||||
} else {
|
||||
String[] parts = valueStr.split("##comma##"); //NON-NLS
|
||||
if (parts.length != 2) {
|
||||
throw new TskCoreException("Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName //NON-NLS
|
||||
+ "(" + valueStr + ")");
|
||||
}
|
||||
return (fieldName + ">" + parts[0] + " AND " + fieldName + "<" + parts[1]); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL clause for a String object
|
||||
*
|
||||
* @param stringObj The full Cybox String object
|
||||
* @param fieldName Name of the field we're testing against
|
||||
*
|
||||
* @return SQL clause
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
|
||||
throws TskCoreException {
|
||||
|
||||
return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
|
||||
stringObj.getApplyCondition(), fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL clause for a String object
|
||||
*
|
||||
* @param valueStr Value as a string
|
||||
* @param condition Cybox condition
|
||||
* @param applyCondition Cybox apply_condition
|
||||
* @param fieldName Name of the field we're testing against
|
||||
*
|
||||
* @return SQL clause
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public static String processStringObject(String valueStr, ConditionTypeEnum condition,
|
||||
ConditionApplicationEnum applyCondition, String fieldName)
|
||||
throws TskCoreException {
|
||||
|
||||
String fullClause = "";
|
||||
String lowerFieldName = "lower(" + fieldName + ")"; //NON-NLS
|
||||
|
||||
if (valueStr.isEmpty()) {
|
||||
throw new TskCoreException("Empty value field"); //NON-NLS
|
||||
}
|
||||
|
||||
String[] parts = valueStr.split("##comma##"); //NON-NLS
|
||||
|
||||
for (String value : parts) {
|
||||
String lowerValue = value.toLowerCase();
|
||||
String partialClause;
|
||||
if ((condition == null)
|
||||
|| (condition == ConditionTypeEnum.EQUALS)) {
|
||||
partialClause = lowerFieldName + "=\'" + lowerValue + "\'";
|
||||
} else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
|
||||
partialClause = lowerFieldName + " !=\'%" + lowerValue + "%\'";
|
||||
} else if (condition == ConditionTypeEnum.CONTAINS) {
|
||||
partialClause = lowerFieldName + " LIKE \'%" + lowerValue + "%\'"; //NON-NLS
|
||||
} else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
|
||||
partialClause = lowerFieldName + " NOT LIKE \'%" + lowerValue + "%\'"; //NON-NLS
|
||||
} else if (condition == ConditionTypeEnum.STARTS_WITH) {
|
||||
partialClause = lowerFieldName + " LIKE \'" + lowerValue + "%\'"; //NON-NLS
|
||||
} else if (condition == ConditionTypeEnum.ENDS_WITH) {
|
||||
partialClause = lowerFieldName + " LIKE \'%" + lowerValue + "\'"; //NON-NLS
|
||||
} else {
|
||||
throw new TskCoreException("Could not process condition " + condition.value() + " on " + fieldName); //NON-NLS
|
||||
}
|
||||
|
||||
if (fullClause.isEmpty()) {
|
||||
|
||||
if (parts.length > 1) {
|
||||
fullClause += "( ";
|
||||
}
|
||||
if (applyCondition == ConditionApplicationEnum.NONE) {
|
||||
fullClause += " NOT "; //NON-NLS
|
||||
}
|
||||
fullClause += partialClause;
|
||||
} else {
|
||||
if (applyCondition == ConditionApplicationEnum.ALL) {
|
||||
fullClause += " AND " + partialClause; //NON-NLS
|
||||
} else if (applyCondition == ConditionApplicationEnum.NONE) {
|
||||
fullClause += " AND NOT " + partialClause; //NON-NLS
|
||||
} else {
|
||||
fullClause += " OR " + partialClause; //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length > 1) {
|
||||
fullClause += " )";
|
||||
}
|
||||
|
||||
return fullClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the SQL clause for a timestamp object. Converts the time into a
|
||||
* numeric field and then creates the clause from that.
|
||||
*
|
||||
* @param dateObj Cybox DateTimeObject
|
||||
* @param fieldName Name of the field we're testing against
|
||||
*
|
||||
* @return SQL clause
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
|
||||
throws TskCoreException {
|
||||
|
||||
if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
|
||||
|
||||
// Change the string into unix timestamps
|
||||
String result = convertTimestampString(dateObj.getValue().toString());
|
||||
return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
|
||||
|
||||
} else {
|
||||
throw new TskCoreException("Found non DATE_TIME field on " + fieldName); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a timestamp string into a numeric one. Leave it as a string since
|
||||
* that's what we get from other object types.
|
||||
*
|
||||
* @param timestampStr
|
||||
*
|
||||
* @return String version with timestamps replaced by numeric values
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static String convertTimestampString(String timestampStr)
|
||||
throws TskCoreException {
|
||||
try {
|
||||
String result = "";
|
||||
if (timestampStr.length() > 0) {
|
||||
String[] parts = timestampStr.split("##comma##"); //NON-NLS
|
||||
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
long unixTime = convertTimestamp(parts[i]);
|
||||
result += unixTime + "##comma##"; //NON-NLS
|
||||
}
|
||||
result += convertTimestamp(parts[parts.length - 1]);
|
||||
}
|
||||
return result;
|
||||
} catch (java.text.ParseException ex) {
|
||||
throw new TskCoreException("Error parsing timestamp string " + timestampStr); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new clause to the existing clause
|
||||
*
|
||||
* @param a_clause Current clause
|
||||
* @param a_newClause New clause
|
||||
*
|
||||
* @return Full clause
|
||||
*/
|
||||
private static String addClause(String a_clause, String a_newClause) {
|
||||
|
||||
if ((a_clause == null) || a_clause.isEmpty()) {
|
||||
return a_newClause;
|
||||
}
|
||||
|
||||
return (a_clause + " AND " + a_newClause); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
|
||||
import org.mitre.cybox.objects.WindowsNetworkShare;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalNetworkShareObj extends EvaluatableObject {
|
||||
|
||||
private final WindowsNetworkShare obj;
|
||||
|
||||
public EvalNetworkShareObj(WindowsNetworkShare a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
if ((obj.getNetname() == null) && (obj.getLocalPath() == null)) {
|
||||
return new ObservableResult(id, "NetworkShareObjet: No remote name or local path found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// For displaying what we were looking for in the results
|
||||
String searchString = "";
|
||||
if (obj.getNetname() != null) {
|
||||
searchString += "Netname \"" + obj.getNetname().getValue() + "\""; //NON-NLS
|
||||
|
||||
// The apply conditions ALL or NONE probably won't work correctly. Neither seems
|
||||
// all that likely to come up in practice, so just give a warning.
|
||||
if ((obj.getNetname().getApplyCondition() != null)
|
||||
&& (obj.getNetname().getApplyCondition() != ConditionApplicationEnum.ANY)) {
|
||||
addWarning("Apply condition " + obj.getNetname().getApplyCondition().value() //NON-NLS
|
||||
+ " may not work correctly"); //NON-NLS
|
||||
}
|
||||
}
|
||||
if (obj.getLocalPath() != null) {
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "LocalPath \"" + obj.getLocalPath().getValue() + "\""; //NON-NLS
|
||||
|
||||
// Same as above - the apply conditions ALL or NONE probably won't work correctly. Neither seems
|
||||
// all that likely to come up in practice, so just give a warning.
|
||||
if ((obj.getLocalPath().getApplyCondition() != null)
|
||||
&& (obj.getLocalPath().getApplyCondition() != ConditionApplicationEnum.ANY)) {
|
||||
addWarning("Apply condition " + obj.getLocalPath().getApplyCondition().value() //NON-NLS
|
||||
+ " may not work correctly"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
// The assumption here is that there aren't going to be too many network shares, so we
|
||||
// can cycle through all of them.
|
||||
try {
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_REMOTE_DRIVE);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
boolean foundRemotePathMatch = false;
|
||||
boolean foundLocalPathMatch = false;
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REMOTE_PATH.getTypeID())
|
||||
&& (obj.getNetname() != null)) {
|
||||
foundRemotePathMatch = compareStringObject(obj.getNetname(), attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCAL_PATH.getTypeID())
|
||||
&& (obj.getLocalPath() != null)) {
|
||||
foundLocalPathMatch = compareStringObject(obj.getLocalPath(), attr.getValueString());
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we found everything we were looking for
|
||||
if (((foundRemotePathMatch) || (obj.getNetname() == null))
|
||||
&& ((foundLocalPathMatch) || (obj.getLocalPath() == null))) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we found any matches
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "NetworkShare")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "NetworkShareObject: Found a match for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Didn't find any matches
|
||||
return new ObservableResult(id, "NetworkObject: No matches found for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "NetworkObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
|
||||
if (obj.getCurrentUses() != null) {
|
||||
fieldNames.add("Current_Uses"); //NON-NLS
|
||||
}
|
||||
if (obj.getMaxUses() != null) {
|
||||
fieldNames.add("Max_Uses"); //NON-NLS
|
||||
}
|
||||
if (obj.getType() != null) {
|
||||
fieldNames.add("Type"); //NON-NLS
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
@ -1,485 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import com.williballenthin.rejistry.RegistryHiveFile;
|
||||
import com.williballenthin.rejistry.RegistryKey;
|
||||
import com.williballenthin.rejistry.RegistryParseException;
|
||||
import com.williballenthin.rejistry.RegistryValue;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import org.mitre.cybox.objects.WindowsRegistryKey;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalRegistryObj extends EvaluatableObject {
|
||||
|
||||
private final WindowsRegistryKey obj;
|
||||
private final List<RegistryFileInfo> regFiles = new ArrayList<>();
|
||||
|
||||
EvalRegistryObj(WindowsRegistryKey a_obj, String a_id, String a_spacing, List<RegistryFileInfo> a_regFiles) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
regFiles.addAll(a_regFiles);
|
||||
}
|
||||
|
||||
private EvalRegistryObj() {
|
||||
obj = null;
|
||||
id = null;
|
||||
spacing = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
// Key name is required
|
||||
if (obj.getKey() == null) {
|
||||
return new ObservableResult(id, "RegistryObject: No key found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// For now, only support a full string match
|
||||
if (!((obj.getKey().getCondition() == null)
|
||||
|| (obj.getKey().getCondition() == ConditionTypeEnum.EQUALS))) {
|
||||
return new ObservableResult(id, "RegistryObject: Can not support condition " + obj.getKey().getCondition() //NON-NLS
|
||||
+ " on Key field", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
// Make a list of hives to test
|
||||
List<RegistryFileInfo> hiveList = new ArrayList<>();
|
||||
if (obj.getHive() == null) {
|
||||
// If the hive field is missing, add everything
|
||||
hiveList.addAll(regFiles);
|
||||
} else if (obj.getHive().getValue().toString().startsWith("HKEY")) { //NON-NLS
|
||||
// If the hive name is HKEY_LOCAL_MACHINE, add the ones from the config directory.
|
||||
// Otherwise, add the others
|
||||
for (RegistryFileInfo regFile : regFiles) {
|
||||
if (regFile.getAbstractFile().getParentPath() != null) {
|
||||
Pattern pattern = Pattern.compile("system32", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(regFile.getAbstractFile().getParentPath());
|
||||
if (matcher.find()) {
|
||||
// Looking for system files and found one, so add it to the list
|
||||
if (obj.getHive().getValue().toString().equalsIgnoreCase("HKEY_LOCAL_MACHINE")) { //NON-NLS
|
||||
hiveList.add(regFile);
|
||||
}
|
||||
} else {
|
||||
// Looking for non-system files and found one, so add it to the list
|
||||
if (!obj.getHive().getValue().toString().equalsIgnoreCase("HKEY_LOCAL_MACHINE")) { //NON-NLS
|
||||
hiveList.add(regFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, try to match the name
|
||||
String stixHiveName = obj.getHive().getValue().toString();
|
||||
|
||||
// The temp files will end \Temp\STIX\(hive)_(number)
|
||||
Pattern pattern = Pattern.compile("Temp.STIX." + stixHiveName, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
for (RegistryFileInfo hive : regFiles) {
|
||||
Matcher matcher = pattern.matcher(hive.getTempFileName());
|
||||
if (matcher.find()) {
|
||||
hiveList.add(hive);
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing matched, add all the files
|
||||
if (hiveList.isEmpty()) {
|
||||
hiveList.addAll(regFiles);
|
||||
}
|
||||
}
|
||||
|
||||
// This is unlikely to happen unless we have no registry files to test against
|
||||
if (hiveList.isEmpty()) {
|
||||
return new ObservableResult(id, "RegistryObject: No matching registry hives found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
for (RegistryFileInfo hive : hiveList) {
|
||||
try {
|
||||
ObservableResult result = testRegistryFile(hive);
|
||||
if (result.isTrue()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// The registry parser seems to throw lots of different types of exceptions,
|
||||
// so make sure to catch them all by this point. Malformed registry files
|
||||
// in particular cause problems.
|
||||
addWarning("Error processing registry file " + hive); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.getHive() == null) {
|
||||
return new ObservableResult(id, "RegistryObject: Could not find key " + obj.getKey().getValue(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
return new ObservableResult(id, "RegistryObject: Could not find key " + obj.getKey().getValue() //NON-NLS
|
||||
+ " in hive " + obj.getHive().getValue(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Registry object against one registry file.
|
||||
*
|
||||
* @param a_regInfo The registry file
|
||||
*
|
||||
* @return Result of the test
|
||||
*/
|
||||
private ObservableResult testRegistryFile(RegistryFileInfo a_regInfo) {
|
||||
try {
|
||||
RegistryKey root = openRegistry(a_regInfo.getTempFileName());
|
||||
RegistryKey result = findKey(root, obj.getKey().getValue().toString());
|
||||
|
||||
if (result == null) {
|
||||
|
||||
// Take another shot looking for the key minus the first part of the path (sometimes the
|
||||
// hive file name is here). This should only happen if the hive name started
|
||||
// with "HKEY"
|
||||
if ((obj.getHive() != null)
|
||||
&& obj.getHive().getValue().toString().startsWith("HKEY")) { //NON-NLS
|
||||
String[] parts = obj.getKey().getValue().toString().split("\\\\");
|
||||
String newKey = "";
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
if (newKey.length() > 0) {
|
||||
newKey += "\\";
|
||||
}
|
||||
newKey += parts[i];
|
||||
}
|
||||
result = findKey(root, newKey);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return new ObservableResult(id, "RegistryObject: Could not find key " + obj.getKey().getValue(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
}
|
||||
|
||||
if ((obj.getValues() == null) || (obj.getValues().getValues().isEmpty())) {
|
||||
// No values to test
|
||||
List<StixArtifactData> artData = new ArrayList<>();
|
||||
artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS
|
||||
return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Test all the values
|
||||
for (org.mitre.cybox.objects.RegistryValueType stixRegValue : obj.getValues().getValues()) {
|
||||
try {
|
||||
for (RegistryValue valFromFile : result.getValueList()) {
|
||||
|
||||
// Test if the name field matches (if present)
|
||||
boolean nameSuccess = true; // True if the name matches or isn't present
|
||||
if (stixRegValue.getName() != null) {
|
||||
try {
|
||||
nameSuccess = compareStringObject(stixRegValue.getName(), valFromFile.getName());
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
nameSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean valueSuccess = true;
|
||||
if (nameSuccess && (stixRegValue.getData() != null)) {
|
||||
switch (valFromFile.getValueType()) {
|
||||
case REG_SZ:
|
||||
case REG_EXPAND_SZ:
|
||||
|
||||
try {
|
||||
valueSuccess = compareStringObject(stixRegValue.getData(),
|
||||
valFromFile.getValue().getAsString());
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
valueSuccess = false;
|
||||
}
|
||||
break;
|
||||
case REG_DWORD:
|
||||
case REG_BIG_ENDIAN:
|
||||
case REG_QWORD:
|
||||
|
||||
// Only support "equals" for now.
|
||||
if ((stixRegValue.getData().getCondition() == null)
|
||||
|| (stixRegValue.getData().getCondition() == ConditionTypeEnum.EQUALS)) {
|
||||
|
||||
// Try to convert the STIX string to a long
|
||||
try {
|
||||
long stixValue = Long.decode(stixRegValue.getData().getValue().toString());
|
||||
|
||||
try {
|
||||
valueSuccess = (stixValue == valFromFile.getValue().getAsNumber());
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
valueSuccess = false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
// We probably weren't looking at a numeric field to begin with,
|
||||
// so getting this exception isn't really an error.
|
||||
valueSuccess = false;
|
||||
}
|
||||
} else {
|
||||
valueSuccess = false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// Nothing to do here. These are the types we don't handle:
|
||||
// REG_BIN, REG_FULL_RESOURCE_DESCRIPTOR, REG_LINK, REG_MULTI_SZ, REG_NONE,
|
||||
// REG_RESOURCE_LIST, REG_RESOURCE_REQUIREMENTS_LIST
|
||||
}
|
||||
}
|
||||
|
||||
if (nameSuccess && valueSuccess) {
|
||||
// Found a match for all values
|
||||
List<StixArtifactData> artData = new ArrayList<>();
|
||||
artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS
|
||||
return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue() //NON-NLS
|
||||
+ " and value " + stixRegValue.getName().getValue().toString() //NON-NLS
|
||||
+ " = " + stixRegValue.getData().getValue().toString(),
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// Broad catch here becase the registry parser can create all kinds of exceptions beyond what it reports.
|
||||
return new ObservableResult(id, "RegistryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
return new ObservableResult(id, "RegistryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "RegistryObject: Not done", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
public RegistryKey openRegistry(String hive) throws TskCoreException {
|
||||
|
||||
try {
|
||||
RegistryHiveFile regFile = new RegistryHiveFile(new File(hive));
|
||||
RegistryKey root = regFile.getRoot();
|
||||
return root;
|
||||
} catch (IOException ex) {
|
||||
throw new TskCoreException("Error opening registry file - " + ex.getLocalizedMessage()); //NON-NLS
|
||||
} catch (RegistryParseException ex) {
|
||||
throw new TskCoreException("Error opening root node of registry - " + ex.getLocalizedMessage()); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go down the registry tree to find a key with the given name.
|
||||
*
|
||||
* @param root Root of the registry hive
|
||||
* @param name Name of the subkey to seach for
|
||||
*
|
||||
* @return The matching subkey or null if not found
|
||||
*/
|
||||
public RegistryKey findKey(RegistryKey root, String name) {
|
||||
|
||||
RegistryKey currentKey = root;
|
||||
|
||||
// Split the key name into parts
|
||||
String[] parts = name.split("\\\\");
|
||||
for (String part : parts) {
|
||||
|
||||
if (part.length() > 0) {
|
||||
try {
|
||||
currentKey = currentKey.getSubkey(part);
|
||||
} catch (Exception ex) {
|
||||
// We get an exception if the value wasn't found (not a RegistryParseException).
|
||||
// There doesn't seem to be a cleaner way to test for existance without cycling though
|
||||
// everything ourselves. (Broad catch because things other than RegistryParseException
|
||||
// can happen)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we make it this far, we've found it
|
||||
return currentKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all registry hives to the temp directory and return the list of
|
||||
* created files.
|
||||
*
|
||||
* @return Paths to copied registry files.
|
||||
*/
|
||||
public static List<RegistryFileInfo> copyRegistryFiles() throws TskCoreException {
|
||||
|
||||
// First get all the abstract files
|
||||
List<AbstractFile> regFilesAbstract = findRegistryFiles();
|
||||
|
||||
// List to hold all the extracted file names plus their abstract file
|
||||
List<RegistryFileInfo> regFilesLocal = new ArrayList<>();
|
||||
|
||||
// Make the temp directory
|
||||
String tmpDir;
|
||||
try {
|
||||
tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + "STIX"; //NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException(ex.getLocalizedMessage());
|
||||
}
|
||||
File dir = new File(tmpDir);
|
||||
if (dir.exists() == false) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
long index = 1;
|
||||
for (AbstractFile regFile : regFilesAbstract) {
|
||||
String regFileName = regFile.getName();
|
||||
String regFileNameLocal = tmpDir + File.separator + regFileName + "_" + index;
|
||||
File regFileNameLocalFile = new File(regFileNameLocal);
|
||||
try {
|
||||
// Don't save any unallocated versions
|
||||
if (regFile.getMetaFlagsAsString().contains("Allocated")) { //NON-NLS
|
||||
ContentUtils.writeToFile(regFile, regFileNameLocalFile);
|
||||
regFilesLocal.add(new EvalRegistryObj().new RegistryFileInfo(regFile, regFileNameLocal));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new TskCoreException(ex.getLocalizedMessage());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return regFilesLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the registry hives on the system. Mostly copied from
|
||||
* RecentActivity
|
||||
*/
|
||||
private static List<AbstractFile> findRegistryFiles() throws TskCoreException {
|
||||
List<AbstractFile> registryFiles = new ArrayList<>();
|
||||
Case openCase;
|
||||
try {
|
||||
openCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException(ex.getLocalizedMessage());
|
||||
}
|
||||
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = openCase.getServices().getFileManager();
|
||||
|
||||
for (Content ds : openCase.getDataSources()) {
|
||||
|
||||
// find the user-specific ntuser-dat files
|
||||
registryFiles.addAll(fileManager.findFiles(ds, "ntuser.dat")); //NON-NLS
|
||||
|
||||
// find the system hives
|
||||
String[] regFileNames = new String[]{"system", "software", "security", "sam"}; //NON-NLS
|
||||
for (String regFileName : regFileNames) {
|
||||
List<AbstractFile> allRegistryFiles = fileManager.findFiles(ds, regFileName, "/system32/config"); //NON-NLS
|
||||
for (AbstractFile regFile : allRegistryFiles) {
|
||||
// Don't want anything from regback
|
||||
if (!regFile.getParentPath().contains("RegBack")) { //NON-NLS
|
||||
registryFiles.add(regFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return registryFiles;
|
||||
}
|
||||
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<>();
|
||||
|
||||
if (obj.getNumberValues() != null) {
|
||||
fieldNames.add("Number_Values"); //NON-NLS
|
||||
}
|
||||
if (obj.getModifiedTime() != null) {
|
||||
fieldNames.add("Modified_Time"); //NON-NLS
|
||||
}
|
||||
if (obj.getCreatorUsername() != null) {
|
||||
fieldNames.add("Creator_Username"); //NON-NLS
|
||||
}
|
||||
if (obj.getHandleList() != null) {
|
||||
fieldNames.add("Handle_List"); //NON-NLS
|
||||
}
|
||||
if (obj.getNumberSubkeys() != null) {
|
||||
fieldNames.add("Number_Subkeys"); //NON-NLS
|
||||
}
|
||||
if (obj.getSubkeys() != null) {
|
||||
fieldNames.add("Subkeys"); //NON-NLS
|
||||
}
|
||||
if (obj.getByteRuns() != null) {
|
||||
fieldNames.add("Byte_Runs"); //NON-NLS
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to keep track of the abstract file and temp file that goes with
|
||||
* each registry hive.
|
||||
*/
|
||||
public class RegistryFileInfo {
|
||||
|
||||
private final AbstractFile abstractFile;
|
||||
private final String tempFileName;
|
||||
|
||||
public RegistryFileInfo(AbstractFile a_abstractFile, String a_tempFileName) {
|
||||
abstractFile = a_abstractFile;
|
||||
tempFileName = a_tempFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AbstractFile for this RegistryFileInfo
|
||||
*
|
||||
* @return the abstractFile
|
||||
*/
|
||||
AbstractFile getAbstractFile() {
|
||||
return abstractFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Temporary file name for this RegistryFileInfo
|
||||
*
|
||||
* @return the tempFileName
|
||||
*/
|
||||
String getTempFileName() {
|
||||
return tempFileName;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.OSInfo;
|
||||
import org.sleuthkit.datamodel.OSUtility;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.mitre.cybox.objects.SystemObjectType;
|
||||
import org.mitre.cybox.objects.WindowsSystem;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalSystemObj extends EvaluatableObject {
|
||||
|
||||
private final SystemObjectType obj;
|
||||
|
||||
public EvalSystemObj(SystemObjectType a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
// For displaying what we were looking for in the results
|
||||
String searchString = "";
|
||||
|
||||
// Check which fields are present and record them
|
||||
boolean haveHostname = false;
|
||||
// boolean haveDomain = false;
|
||||
boolean haveProcArch = false;
|
||||
boolean haveTempDir = false;
|
||||
boolean haveProductName = false;
|
||||
boolean haveSystemRoot = false;
|
||||
boolean haveProductID = false;
|
||||
boolean haveOwner = false;
|
||||
boolean haveOrganization = false;
|
||||
|
||||
if (obj.getHostname() != null) {
|
||||
haveHostname = true;
|
||||
searchString = "Hostname \"" + obj.getHostname().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (obj.getProcessorArchitecture() != null) {
|
||||
haveProcArch = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Processor architecture \"" + obj.getProcessorArchitecture().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
WindowsSystem winSysObj = null;
|
||||
if (obj instanceof WindowsSystem) {
|
||||
winSysObj = (WindowsSystem) obj;
|
||||
|
||||
if (winSysObj.getProductID() != null) {
|
||||
haveProductID = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Product ID \"" + winSysObj.getProductID().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (winSysObj.getProductName() != null) {
|
||||
haveProductName = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Product Name \"" + winSysObj.getProductName().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (winSysObj.getRegisteredOrganization() != null) {
|
||||
haveOrganization = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Registered Org \"" + winSysObj.getRegisteredOrganization().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (winSysObj.getRegisteredOwner() != null) {
|
||||
haveOwner = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Registered Owner \"" + winSysObj.getRegisteredOwner().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (winSysObj.getWindowsSystemDirectory() != null) {
|
||||
haveSystemRoot = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "System root \"" + winSysObj.getWindowsSystemDirectory().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
if (winSysObj.getWindowsTempDirectory() != null) {
|
||||
haveTempDir = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Temp dir \"" + winSysObj.getWindowsTempDirectory().getValue().toString() + "\""; //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
// Return if we have nothing to search for
|
||||
if (!(haveHostname || haveProcArch
|
||||
|| haveTempDir || haveProductName || haveSystemRoot || haveProductID
|
||||
|| haveOwner || haveOrganization)) {
|
||||
return new ObservableResult(id, "SystemObject: No evaluatable fields found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
try {
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
List<OSInfo> osInfoList = OSUtility.getOSInfo(sleuthkitCase);
|
||||
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
if (!osInfoList.isEmpty()) {
|
||||
for (OSInfo info : osInfoList) {
|
||||
|
||||
boolean foundHostnameMatch = false;
|
||||
//boolean foundDomainMatch = false;
|
||||
boolean foundProcArchMatch = false;
|
||||
boolean foundTempDirMatch = false;
|
||||
boolean foundProductNameMatch = false;
|
||||
boolean foundSystemRootMatch = false;
|
||||
boolean foundProductIDMatch = false;
|
||||
boolean foundOwnerMatch = false;
|
||||
boolean foundOrganizationMatch = false;
|
||||
|
||||
if (haveHostname) {
|
||||
foundHostnameMatch = compareStringObject(obj.getHostname(), info.getCompName());
|
||||
}
|
||||
if (haveProcArch) {
|
||||
foundProcArchMatch = compareStringObject(obj.getProcessorArchitecture().getValue().toString(),
|
||||
obj.getProcessorArchitecture().getCondition(),
|
||||
obj.getProcessorArchitecture().getApplyCondition(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE));
|
||||
}
|
||||
if (haveTempDir && (winSysObj != null)) {
|
||||
foundTempDirMatch = compareStringObject(winSysObj.getWindowsTempDirectory(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEMP_DIR));
|
||||
}
|
||||
if (haveProductName && (winSysObj != null)) {
|
||||
foundProductNameMatch = compareStringObject(winSysObj.getProductName(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
|
||||
}
|
||||
if (haveSystemRoot && (winSysObj != null)) {
|
||||
foundSystemRootMatch = compareStringObject(winSysObj.getWindowsSystemDirectory(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH));
|
||||
}
|
||||
if (haveProductID && (winSysObj != null)) {
|
||||
foundProductIDMatch = compareStringObject(winSysObj.getProductID(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PRODUCT_ID));
|
||||
}
|
||||
if (haveOwner && (winSysObj != null)) {
|
||||
foundOwnerMatch = compareStringObject(winSysObj.getRegisteredOwner(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OWNER));
|
||||
}
|
||||
if (haveOrganization && (winSysObj != null)) {
|
||||
foundOrganizationMatch = compareStringObject(winSysObj.getRegisteredOrganization(),
|
||||
info.getAttributeValue(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ORGANIZATION));
|
||||
}
|
||||
|
||||
if (((!haveHostname) || foundHostnameMatch)
|
||||
&& ((!haveProcArch) || foundProcArchMatch)
|
||||
&& ((!haveTempDir) || foundTempDirMatch)
|
||||
&& ((!haveProductName) || foundProductNameMatch)
|
||||
&& ((!haveSystemRoot) || foundSystemRootMatch)
|
||||
&& ((!haveProductID) || foundProductIDMatch)
|
||||
&& ((!haveOwner) || foundOwnerMatch)
|
||||
&& ((!haveOrganization) || foundOrganizationMatch)) {
|
||||
|
||||
finalHits.addAll(info.getArtifacts());
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "System")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "SystemObject: Found a match for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Didn't find any matches
|
||||
return new ObservableResult(id, "SystemObject: No matches found for " + searchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} else {
|
||||
return new ObservableResult(id, "SystemObject: No OS artifacts found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "SystemObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the warning for any fields in the object that aren't supported.
|
||||
*/
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
|
||||
if (obj.getAvailablePhysicalMemory() != null) {
|
||||
fieldNames.add("Available_Physical_Memory"); //NON-NLS
|
||||
}
|
||||
if (obj.getBIOSInfo() != null) {
|
||||
fieldNames.add("BIOS_Info"); //NON-NLS
|
||||
}
|
||||
if (obj.getDate() != null) {
|
||||
fieldNames.add("Date"); //NON-NLS
|
||||
}
|
||||
if (obj.getLocalTime() != null) {
|
||||
fieldNames.add("Local_Time"); //NON-NLS
|
||||
}
|
||||
if (obj.getNetworkInterfaceList() != null) {
|
||||
fieldNames.add("Network_Interface_List"); //NON-NLS
|
||||
}
|
||||
if (obj.getOS() != null) {
|
||||
fieldNames.add("OS"); //NON-NLS
|
||||
}
|
||||
if (obj.getProcessor() != null) {
|
||||
fieldNames.add("Processor"); //NON-NLS
|
||||
}
|
||||
if (obj.getSystemTime() != null) {
|
||||
fieldNames.add("System_Time"); //NON-NLS
|
||||
}
|
||||
if (obj.getTimezoneDST() != null) {
|
||||
fieldNames.add("Timezone_DST"); //NON-NLS
|
||||
}
|
||||
if (obj.getTimezoneStandard() != null) {
|
||||
fieldNames.add("Timezone_Standard"); //NON-NLS
|
||||
}
|
||||
if (obj.getTotalPhysicalMemory() != null) {
|
||||
fieldNames.add("Total_Physical_Memory"); //NON-NLS
|
||||
}
|
||||
if (obj.getUptime() != null) {
|
||||
fieldNames.add("Uptime"); //NON-NLS
|
||||
}
|
||||
if (obj.getUsername() != null) {
|
||||
fieldNames.add("Username"); //NON-NLS
|
||||
}
|
||||
|
||||
if (obj instanceof WindowsSystem) {
|
||||
WindowsSystem winSysObj = (WindowsSystem) obj;
|
||||
|
||||
if (winSysObj.getDomains() != null) {
|
||||
fieldNames.add("Domain"); //NON-NLS
|
||||
}
|
||||
if (winSysObj.getGlobalFlagList() != null) {
|
||||
fieldNames.add("Global_Flag_List"); //NON-NLS
|
||||
}
|
||||
if (winSysObj.getNetBIOSName() != null) {
|
||||
fieldNames.add("NetBIOS_Name"); //NON-NLS
|
||||
}
|
||||
if (winSysObj.getOpenHandleList() != null) {
|
||||
fieldNames.add("Open_Handle_List"); //NON-NLS
|
||||
}
|
||||
if (winSysObj.getWindowsDirectory() != null) {
|
||||
fieldNames.add("Windows_Directory"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
|
||||
import org.mitre.cybox.objects.URIObjectType;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalURIObj extends EvaluatableObject {
|
||||
|
||||
private final URIObjectType obj;
|
||||
|
||||
public EvalURIObj(URIObjectType a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
if (obj.getValue() == null) {
|
||||
return new ObservableResult(id, "URIObject: No URI value field found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
Case case1;
|
||||
try {
|
||||
case1 = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "Exception while getting open case: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
}
|
||||
|
||||
String addressStr = obj.getValue().getValue().toString();
|
||||
|
||||
// Strip off http:// or https://
|
||||
String modifiedAddressStr = addressStr.toLowerCase();
|
||||
modifiedAddressStr = modifiedAddressStr.replaceAll("http(s)?://", ""); //NON-NLS
|
||||
|
||||
// Since we have single URL artifacts, ALL and NONE conditions probably don't make sense to test
|
||||
if (!((obj.getValue().getApplyCondition() == null)
|
||||
|| (obj.getValue().getApplyCondition() == ConditionApplicationEnum.ANY))) {
|
||||
return new ObservableResult(id, "URIObject: Can not process apply condition " + obj.getValue().getApplyCondition().toString() //NON-NLS
|
||||
+ " on URI object", spacing, ObservableResult.ObservableState.INDETERMINATE, null); //NON-NLS
|
||||
}
|
||||
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
|
||||
try {
|
||||
/*
|
||||
* if ((obj.getValue().getCondition() == null) ||
|
||||
* (obj.getValue().getCondition() == ConditionTypeEnum.EQUALS)) {
|
||||
*
|
||||
* // Old version - uses a database query but only works on full
|
||||
* strings. // It will be faster to use this in the "equals" case
|
||||
* String[] parts = addressStr.split("##comma##");
|
||||
* List<BlackboardArtifact> arts = new
|
||||
* ArrayList<BlackboardArtifact>(); for (String part : parts) {
|
||||
* arts.addAll(sleuthkitCase.getBlackboardArtifacts(
|
||||
* BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT,
|
||||
* BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, part)); }
|
||||
*
|
||||
* if (!arts.isEmpty()) {
|
||||
*
|
||||
* List<StixArtifactData> artData = new
|
||||
* ArrayList<StixArtifactData>(); for (BlackboardArtifact a : arts)
|
||||
* { artData.add(new StixArtifactData(a.getObjectID(), id,
|
||||
* "URIObject")); }
|
||||
*
|
||||
* return new ObservableResult(id, "URIObject: Found " + arts.size()
|
||||
* + " matches for address = \"" + addressStr + "\"", spacing,
|
||||
* ObservableResult.ObservableState.TRUE, artData);
|
||||
*
|
||||
* } else { return new ObservableResult(id, "URIObject: Found no
|
||||
* matches for address = \"" + addressStr + "\"", spacing,
|
||||
* ObservableResult.ObservableState.FALSE, null); } } else {
|
||||
*/
|
||||
|
||||
// This is inefficient, but the easiest way to do it.
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
// Get all the URL artifacts
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
|
||||
|
||||
String modifiedAttrString = attr.getValueString();
|
||||
if (modifiedAttrString != null) {
|
||||
modifiedAttrString = modifiedAttrString.toLowerCase();
|
||||
modifiedAttrString = modifiedAttrString.replaceAll("http(s)?://", ""); //NON-NLS
|
||||
}
|
||||
|
||||
if (compareStringObject(modifiedAddressStr, obj.getValue().getCondition(),
|
||||
obj.getValue().getApplyCondition(), modifiedAttrString)) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "UriObject")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "UriObject: Found a match for " + addressStr, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
return new ObservableResult(id, "URIObject: Found no matches for " + addressStr, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
/*
|
||||
* }
|
||||
*/
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
return new ObservableResult(id, "URIObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.mitre.cybox.common_2.AnyURIObjectPropertyType;
|
||||
import org.mitre.cybox.objects.URLHistory;
|
||||
import org.mitre.cybox.objects.URLHistoryEntryType;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EvalURLHistoryObj extends EvaluatableObject {
|
||||
|
||||
private final URLHistory obj;
|
||||
|
||||
public EvalURLHistoryObj(URLHistory a_obj, String a_id, String a_spacing) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ObservableResult evaluate() {
|
||||
|
||||
setWarnings("");
|
||||
|
||||
if ((obj.getBrowserInformation() == null) && (obj.getURLHistoryEntries() == null)) {
|
||||
return new ObservableResult(id, "URLHistoryObject: No browser info or history entries found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// For displaying what we were looking for in the results
|
||||
String baseSearchString = "";
|
||||
String finalResultsStr = "";
|
||||
|
||||
// The browser info is the same for each entry
|
||||
boolean haveBrowserName = false;
|
||||
if (obj.getBrowserInformation() != null) {
|
||||
if (obj.getBrowserInformation().getName() != null) {
|
||||
haveBrowserName = true;
|
||||
}
|
||||
baseSearchString = "Browser \"" + obj.getBrowserInformation().getName() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
// Matching artifacts
|
||||
List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>();
|
||||
|
||||
if (obj.getURLHistoryEntries() != null) {
|
||||
|
||||
for (URLHistoryEntryType entry : obj.getURLHistoryEntries()) {
|
||||
|
||||
boolean haveURL = false;
|
||||
boolean haveHostname = false;
|
||||
boolean haveReferrer = false;
|
||||
boolean havePageTitle = false;
|
||||
boolean haveUserProfile = false;
|
||||
|
||||
setUnsupportedEntryFieldWarnings(entry);
|
||||
|
||||
// At present, the search string doesn't get reported (because there could be different ones
|
||||
// for multiple URL History Entries) but it's good for debugging.
|
||||
String searchString = baseSearchString;
|
||||
|
||||
if ((entry.getURL() != null) && (entry.getURL().getValue() != null)) {
|
||||
haveURL = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "URL \"" + entry.getURL().getValue().getValue() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
if ((entry.getReferrerURL() != null) && (entry.getReferrerURL().getValue() != null)) {
|
||||
haveReferrer = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Referrer \"" + entry.getReferrerURL().getValue().getValue() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
if (entry.getUserProfileName() != null) {
|
||||
haveUserProfile = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "UserProfile \"" + entry.getUserProfileName().getValue() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
if (entry.getPageTitle() != null) {
|
||||
havePageTitle = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Page title \"" + entry.getPageTitle().getValue() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
if ((entry.getHostname() != null) && (entry.getHostname().getHostnameValue() != null)) {
|
||||
haveHostname = true;
|
||||
if (!searchString.isEmpty()) {
|
||||
searchString += " and "; //NON-NLS
|
||||
}
|
||||
searchString += "Hostname \"" + entry.getHostname().getHostnameValue().getValue() + "\""; //NON-NLS
|
||||
}
|
||||
|
||||
if (!finalResultsStr.isEmpty()) {
|
||||
finalResultsStr += ", ";
|
||||
}
|
||||
finalResultsStr += searchString;
|
||||
|
||||
if (!(haveURL || haveHostname || haveReferrer
|
||||
|| havePageTitle || haveUserProfile || haveBrowserName)) {
|
||||
return new ObservableResult(id, "URLHistoryObject: No evaluatable fields found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
try {
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
boolean foundURLMatch = false;
|
||||
boolean foundHostnameMatch = false;
|
||||
boolean foundReferrerMatch = false;
|
||||
boolean foundPageTitleMatch = false;
|
||||
boolean foundUserProfileMatch = false;
|
||||
boolean foundBrowserNameMatch = false;
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID())
|
||||
&& (haveURL)) {
|
||||
if (entry.getURL().getValue() instanceof AnyURIObjectPropertyType) {
|
||||
foundURLMatch = compareStringObject(entry.getURL().getValue().getValue().toString(),
|
||||
entry.getURL().getValue().getCondition(),
|
||||
entry.getURL().getValue().getApplyCondition(),
|
||||
attr.getValueString());
|
||||
} else {
|
||||
addWarning("Non-AnyURIObjectPropertyType found in URL value field"); //NON-NLS
|
||||
}
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID())
|
||||
&& (haveHostname)) {
|
||||
foundHostnameMatch = compareStringObject(entry.getHostname().getHostnameValue(),
|
||||
attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_REFERRER.getTypeID())
|
||||
&& (haveReferrer)) {
|
||||
if (entry.getReferrerURL().getValue() instanceof AnyURIObjectPropertyType) {
|
||||
foundReferrerMatch = compareStringObject(entry.getReferrerURL().getValue().getValue().toString(),
|
||||
entry.getURL().getValue().getCondition(),
|
||||
entry.getURL().getValue().getApplyCondition(),
|
||||
attr.getValueString());
|
||||
} else {
|
||||
addWarning("Non-AnyURIObjectPropertyType found in URL value field"); //NON-NLS
|
||||
}
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID())
|
||||
&& (havePageTitle)) {
|
||||
foundPageTitleMatch = compareStringObject(entry.getPageTitle(),
|
||||
attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME.getTypeID())
|
||||
&& (haveUserProfile)) {
|
||||
foundUserProfileMatch = compareStringObject(entry.getUserProfileName(),
|
||||
attr.getValueString());
|
||||
}
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID())
|
||||
&& (haveBrowserName)) {
|
||||
foundBrowserNameMatch = compareStringObject(obj.getBrowserInformation().getName(),
|
||||
null, null, attr.getValueString());
|
||||
}
|
||||
}
|
||||
|
||||
if (((!haveURL) || foundURLMatch)
|
||||
&& ((!haveHostname) || foundHostnameMatch)
|
||||
&& ((!haveReferrer) || foundReferrerMatch)
|
||||
&& ((!havePageTitle) || foundPageTitleMatch)
|
||||
&& ((!haveUserProfile) || foundUserProfileMatch)
|
||||
&& ((!haveBrowserName) || foundBrowserNameMatch)) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "URLHistoryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "URLHistory")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "URLHistoryObject: Found at least one match for " + finalResultsStr, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Didn't find any matches
|
||||
return new ObservableResult(id, "URLHistoryObject: No matches found for " + finalResultsStr, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
|
||||
} else if (haveBrowserName) {
|
||||
|
||||
// It doesn't seem too useful, but we can just search for the browser name
|
||||
// if there aren't any URL entries
|
||||
try {
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
List<BlackboardArtifact> artList
|
||||
= sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
||||
|
||||
for (BlackboardArtifact art : artList) {
|
||||
boolean foundBrowserNameMatch = false;
|
||||
|
||||
for (BlackboardAttribute attr : art.getAttributes()) {
|
||||
if ((attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID())
|
||||
&& (haveBrowserName)) {
|
||||
foundBrowserNameMatch = compareStringObject(obj.getBrowserInformation().getName(),
|
||||
null, null, attr.getValueString());
|
||||
}
|
||||
}
|
||||
|
||||
if (foundBrowserNameMatch) {
|
||||
finalHits.add(art);
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalHits.isEmpty()) {
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
for (BlackboardArtifact a : finalHits) {
|
||||
artData.add(new StixArtifactData(a.getObjectID(), id, "URLHistory")); //NON-NLS
|
||||
}
|
||||
return new ObservableResult(id, "URLHistoryObject: Found at least one match", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
|
||||
// Didn't find any matches
|
||||
return new ObservableResult(id, "URLHistoryObject: No matches found for " + baseSearchString, //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.FALSE, null);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
return new ObservableResult(id, "URLHistoryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Nothing to search for
|
||||
return new ObservableResult(id, "URLHistoryObject: No evaluatable fields found", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the warning for any fields in the URL_History_Entry object that
|
||||
* aren't supported.
|
||||
*/
|
||||
private void setUnsupportedEntryFieldWarnings(URLHistoryEntryType entry) {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
|
||||
if (entry.getUserProfileName() != null) {
|
||||
fieldNames.add("User_Profile_Name"); //NON-NLS
|
||||
}
|
||||
if (entry.getVisitCount() != null) {
|
||||
fieldNames.add("Visit_Count"); //NON-NLS
|
||||
}
|
||||
if (entry.getManuallyEnteredCount() != null) {
|
||||
fieldNames.add("Manually_Entered_Count"); //NON-NLS
|
||||
}
|
||||
if (entry.getModificationDateTime() != null) {
|
||||
fieldNames.add("Modification_DateTime"); //NON-NLS
|
||||
}
|
||||
if (entry.getExpirationDateTime() != null) {
|
||||
fieldNames.add("Expiration_DateTime"); //NON-NLS
|
||||
}
|
||||
if (entry.getFirstVisitDateTime() != null) {
|
||||
fieldNames.add("First_Visit_DateTime"); //NON-NLS
|
||||
}
|
||||
if (entry.getLastVisitDateTime() != null) {
|
||||
fieldNames.add("Last_Visit_DateTime"); //NON-NLS
|
||||
}
|
||||
|
||||
String warningStr = "";
|
||||
for (String name : fieldNames) {
|
||||
if (!warningStr.isEmpty()) {
|
||||
warningStr += ", ";
|
||||
}
|
||||
warningStr += name;
|
||||
}
|
||||
|
||||
addWarning("Unsupported URL_History_Entry field(s): " + warningStr); //NON-NLS
|
||||
}
|
||||
}
|
@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mitre.cybox.common_2.ConditionApplicationEnum;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
import org.mitre.cybox.common_2.StringObjectPropertyType;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abstract class EvaluatableObject {
|
||||
|
||||
private String warnings;
|
||||
protected String id;
|
||||
protected String spacing;
|
||||
|
||||
abstract public ObservableResult evaluate();
|
||||
|
||||
/**
|
||||
* Set the warnings string to the given value.
|
||||
*
|
||||
* @param a_warnings
|
||||
*/
|
||||
public void setWarnings(String a_warnings) {
|
||||
warnings = a_warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the warnings string. This should not be used to print the final
|
||||
* version of the warnings.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the warnings string.
|
||||
*
|
||||
* @param a_newWarning
|
||||
*/
|
||||
public void addWarning(String a_newWarning) {
|
||||
if ((warnings == null) || warnings.isEmpty()) {
|
||||
warnings = a_newWarning;
|
||||
return;
|
||||
}
|
||||
warnings = warnings + ", " + a_newWarning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a list of artifacts with the given attribute type that contain the
|
||||
* String Object. All comparisons will look for substrings of the Blackboard
|
||||
* artifacts that match the String Object.
|
||||
*
|
||||
* @param item
|
||||
* @param attrType
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public List<BlackboardArtifact> findArtifactsBySubstring(StringObjectPropertyType item,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE attrType) throws TskCoreException {
|
||||
|
||||
if (item.getValue() == null) {
|
||||
throw new TskCoreException("Error: Value field is null"); //NON-NLS
|
||||
}
|
||||
|
||||
if (item.getCondition() == null) {
|
||||
addWarning("Warning: No condition given for " + attrType.getDisplayName() + " field, using substring comparison"); //NON-NLS
|
||||
} else if (item.getCondition() != ConditionTypeEnum.CONTAINS) {
|
||||
addWarning("Warning: Ignoring condition " + item.getCondition() + " for " //NON-NLS
|
||||
+ attrType.getDisplayName() + " field and doing substring comparison"); //NON-NLS
|
||||
}
|
||||
|
||||
List<BlackboardArtifact> hits = null;
|
||||
try {
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
|
||||
String[] parts = item.getValue().toString().split("##comma##"); //NON-NLS
|
||||
|
||||
if ((item.getApplyCondition() == null)
|
||||
|| (item.getApplyCondition() == ConditionApplicationEnum.ANY)) {
|
||||
|
||||
for (String part : parts) {
|
||||
if (hits == null) {
|
||||
// Note that this searches for artifacts with "part" as a substring
|
||||
hits = sleuthkitCase.getBlackboardArtifacts(
|
||||
attrType,
|
||||
part, false);
|
||||
} else {
|
||||
hits.addAll(sleuthkitCase.getBlackboardArtifacts(
|
||||
attrType,
|
||||
part, false));
|
||||
}
|
||||
}
|
||||
} else if ((item.getApplyCondition() != null)
|
||||
|| (item.getApplyCondition() == ConditionApplicationEnum.ALL)) {
|
||||
|
||||
boolean firstRound = true;
|
||||
for (String part : parts) {
|
||||
if (firstRound) {
|
||||
hits = sleuthkitCase.getBlackboardArtifacts(
|
||||
attrType,
|
||||
part, false);
|
||||
firstRound = false;
|
||||
} else if (hits != null) {
|
||||
hits.retainAll(sleuthkitCase.getBlackboardArtifacts(
|
||||
attrType,
|
||||
part, false));
|
||||
} else {
|
||||
// After first round; hits is still null
|
||||
// I don't think this should happen but if it does we're done
|
||||
return new ArrayList<BlackboardArtifact>();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new TskCoreException("Error: Can not apply NONE condition in search"); //NON-NLS
|
||||
}
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
addWarning(ex.getLocalizedMessage());
|
||||
}
|
||||
|
||||
return hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a CybOX String Object with a given string.
|
||||
*
|
||||
* @param stringObj The CybOX String Object
|
||||
* @param strField The string to compare against
|
||||
*
|
||||
* @return true if strField is a match for the CybOX object
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public static boolean compareStringObject(StringObjectPropertyType stringObj, String strField)
|
||||
throws TskCoreException {
|
||||
if (stringObj.getValue() == null) {
|
||||
throw new TskCoreException("Error: Value field is null"); //NON-NLS
|
||||
}
|
||||
|
||||
String valueStr = stringObj.getValue().toString();
|
||||
ConditionTypeEnum condition = stringObj.getCondition();
|
||||
ConditionApplicationEnum applyCondition = stringObj.getApplyCondition();
|
||||
|
||||
return compareStringObject(valueStr, condition, applyCondition, strField);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a string with CybOX conditions to a given string.
|
||||
*
|
||||
* @param valueStr The CybOX string
|
||||
* @param condition EQUALS, CONTAINS, STARTS_WITH, etc
|
||||
* @param applyCondition ANY, ALL, NONE
|
||||
* @param strField The string to compare against
|
||||
*
|
||||
* @return true if strField is a match for the CybOX valueStr and conditions
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public static boolean compareStringObject(String valueStr, ConditionTypeEnum condition,
|
||||
ConditionApplicationEnum applyCondition, String strField)
|
||||
throws TskCoreException {
|
||||
|
||||
if (valueStr == null) {
|
||||
throw new TskCoreException("Error: Value field is null"); //NON-NLS
|
||||
}
|
||||
|
||||
String[] parts = valueStr.split("##comma##"); //NON-NLS
|
||||
String lowerFieldName = strField.toLowerCase();
|
||||
|
||||
for (String value : parts) {
|
||||
boolean partialResult;
|
||||
if ((condition == null)
|
||||
|| (condition == ConditionTypeEnum.EQUALS)) {
|
||||
partialResult = value.equalsIgnoreCase(strField);
|
||||
} else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
|
||||
partialResult = !value.equalsIgnoreCase(strField);
|
||||
} else if (condition == ConditionTypeEnum.CONTAINS) {
|
||||
partialResult = lowerFieldName.contains(value.toLowerCase());
|
||||
} else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
|
||||
partialResult = !lowerFieldName.contains(value.toLowerCase());
|
||||
} else if (condition == ConditionTypeEnum.STARTS_WITH) {
|
||||
partialResult = lowerFieldName.startsWith(value.toLowerCase());
|
||||
} else if (condition == ConditionTypeEnum.ENDS_WITH) {
|
||||
partialResult = lowerFieldName.endsWith(value.toLowerCase());
|
||||
} else {
|
||||
throw new TskCoreException("Could not process condition " + condition.value() + " on " + value); //NON-NLS
|
||||
}
|
||||
|
||||
// Do all the short-circuiting
|
||||
if (applyCondition == ConditionApplicationEnum.NONE) {
|
||||
if (partialResult == true) {
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
} else if (applyCondition == ConditionApplicationEnum.ALL) {
|
||||
if (partialResult == false) {
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Default is "any"
|
||||
if (partialResult == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we're done and didn't short-circuit, so ALL or NONE conditions were true,
|
||||
// and ANY was false
|
||||
if ((applyCondition == ConditionApplicationEnum.NONE)
|
||||
|| (applyCondition == ConditionApplicationEnum.ALL)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the warnings that will be printed. Basically, just put parentheses
|
||||
* around them if the string isn't empty.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPrintableWarnings() {
|
||||
String warningsToPrint = "";
|
||||
if ((getWarnings() != null)
|
||||
&& (!getWarnings().isEmpty())) {
|
||||
warningsToPrint = " (" + getWarnings() + ")";
|
||||
}
|
||||
return warningsToPrint;
|
||||
}
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.mitre.cybox.cybox_2.OperatorTypeEnum;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ObservableResult {
|
||||
|
||||
public enum ObservableState {
|
||||
|
||||
TRUE("true "), //NON-NLS
|
||||
FALSE("false "), //NON-NLS
|
||||
INDETERMINATE("indeterminate"); //NON-NLS
|
||||
|
||||
private final String label;
|
||||
|
||||
private ObservableState(String s) {
|
||||
label = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableState state = null;
|
||||
private String description = "";
|
||||
private List<StixArtifactData> artifacts;
|
||||
|
||||
public ObservableResult(String a_id, String a_desc, String a_spacing,
|
||||
ObservableState a_state, List<StixArtifactData> a_artifacts) {
|
||||
state = a_state;
|
||||
description = a_spacing + a_id + "\t" + a_state + "\t" + a_desc + "\r\n";
|
||||
artifacts = a_artifacts;
|
||||
}
|
||||
|
||||
public ObservableResult(OperatorTypeEnum a_operator, String a_spacing) {
|
||||
state = ObservableState.INDETERMINATE;
|
||||
description = a_spacing + a_operator + "\r\n";
|
||||
artifacts = new ArrayList<StixArtifactData>();
|
||||
}
|
||||
|
||||
public ObservableState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ObservableResult is currently true. Note: A false
|
||||
* result here does not mean the state is false; it could also be
|
||||
* indeterminate.
|
||||
*
|
||||
* @return true if the ObservableResult is true, false if it is false or
|
||||
* indeterminate
|
||||
*/
|
||||
public boolean isTrue() {
|
||||
return (state == ObservableState.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ObservableResult is currently false. Note: A false
|
||||
* result here does not mean the state is true; it could also be
|
||||
* indeterminate.
|
||||
*
|
||||
* @return true if the ObservableResult is false, false if it is true or
|
||||
* indeterminate
|
||||
*/
|
||||
public boolean isFalse() {
|
||||
return (state == ObservableState.FALSE);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<StixArtifactData> getArtifacts() {
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new result to the current state
|
||||
*
|
||||
* @param a_result The new result to add
|
||||
* @param a_operator AND or OR
|
||||
*/
|
||||
public void addResult(ObservableResult a_result, OperatorTypeEnum a_operator) {
|
||||
addResult(a_result.getDescription(), a_result.getState(),
|
||||
a_result.getArtifacts(), a_operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new result to the current state.
|
||||
*
|
||||
* @param a_description Description of the observable and testing done
|
||||
* @param a_state State of what we're adding (true, false, or
|
||||
* indeterminate)
|
||||
* @param a_operator AND or OR
|
||||
*/
|
||||
private void addResult(String a_description, ObservableState a_state,
|
||||
List<StixArtifactData> a_artifacts, OperatorTypeEnum a_operator) {
|
||||
|
||||
addToDesc(a_description);
|
||||
|
||||
if (a_operator == OperatorTypeEnum.AND) {
|
||||
|
||||
if (a_state == ObservableState.FALSE) {
|
||||
// If we now have a false, the whole thing is false regardless of previous state.
|
||||
// Clear out any existing artifacts.
|
||||
state = ObservableState.FALSE;
|
||||
artifacts.clear();
|
||||
} else if (a_state == ObservableState.INDETERMINATE) {
|
||||
// Don't change the current state, and don't save the new artifacts
|
||||
// (though there probably wouldn't be any)
|
||||
} else {
|
||||
if (state == ObservableState.FALSE) {
|
||||
// Previous state false + new state true => stay false
|
||||
} else if (state == ObservableState.TRUE) {
|
||||
// Previous state true + new state true => stay true and add artifacts
|
||||
if ((artifacts == null) && (a_artifacts != null)) {
|
||||
artifacts = new ArrayList<StixArtifactData>();
|
||||
}
|
||||
if (a_artifacts != null) {
|
||||
artifacts.addAll(a_artifacts);
|
||||
}
|
||||
} else {
|
||||
// If the previous state was indeterminate, change it to true and add artifacts
|
||||
state = ObservableState.TRUE;
|
||||
if ((artifacts == null) && (a_artifacts != null)) {
|
||||
artifacts = new ArrayList<StixArtifactData>();
|
||||
}
|
||||
if (a_artifacts != null) {
|
||||
artifacts.addAll(a_artifacts);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (a_state == ObservableState.TRUE) {
|
||||
// If we now have a true, the whole thing is true regardless of previous state.
|
||||
// Add the new artifacts.
|
||||
state = ObservableState.TRUE;
|
||||
if ((artifacts == null) && (a_artifacts != null)) {
|
||||
artifacts = new ArrayList<StixArtifactData>();
|
||||
}
|
||||
if (a_artifacts != null) {
|
||||
artifacts.addAll(a_artifacts);
|
||||
}
|
||||
} else if (a_state == ObservableState.INDETERMINATE) {
|
||||
// Don't change the current state and don't record it to the
|
||||
// description string (later we should save these in some way)
|
||||
} else {
|
||||
if (state == ObservableState.FALSE) {
|
||||
// Previous state false + new state false => stay false
|
||||
} else if (state == ObservableState.TRUE) {
|
||||
// Previous state true + new state false => stay true
|
||||
} else {
|
||||
// Previous state indeterminate + new state false => change to false
|
||||
state = ObservableState.FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the description string. Mostly just to make things cleaner by not
|
||||
* testing for null all over the place.
|
||||
*
|
||||
* @param a_desc New part of the description to add
|
||||
*/
|
||||
private void addToDesc(String a_desc) {
|
||||
if (description == null) {
|
||||
description = a_desc;
|
||||
} else {
|
||||
description += a_desc;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,699 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 - 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
import org.mitre.cybox.cybox_2.ObjectType;
|
||||
import org.mitre.cybox.cybox_2.Observable;
|
||||
import org.mitre.cybox.cybox_2.ObservableCompositionType;
|
||||
import org.mitre.cybox.cybox_2.OperatorTypeEnum;
|
||||
import org.mitre.cybox.objects.AccountObjectType;
|
||||
import org.mitre.cybox.objects.Address;
|
||||
import org.mitre.cybox.objects.DomainName;
|
||||
import org.mitre.cybox.objects.EmailMessage;
|
||||
import org.mitre.cybox.objects.FileObjectType;
|
||||
import org.mitre.cybox.objects.SystemObjectType;
|
||||
import org.mitre.cybox.objects.URIObjectType;
|
||||
import org.mitre.cybox.objects.URLHistory;
|
||||
import org.mitre.cybox.objects.WindowsNetworkShare;
|
||||
import org.mitre.cybox.objects.WindowsRegistryKey;
|
||||
import org.mitre.stix.common_1.IndicatorBaseType;
|
||||
import org.mitre.stix.indicator_2.Indicator;
|
||||
import org.mitre.stix.stix_1.STIXPackage;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportSettings;
|
||||
import org.sleuthkit.autopsy.report.NoReportModuleSettings;
|
||||
import org.sleuthkit.autopsy.report.ReportModuleSettings;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class STIXReportModule implements GeneralReportModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(STIXReportModule.class.getName());
|
||||
private STIXReportModuleConfigPanel configPanel;
|
||||
private static STIXReportModule instance = null;
|
||||
private String reportPath;
|
||||
private boolean reportAllResults;
|
||||
|
||||
private Map<String, ObjectType> idToObjectMap = new HashMap<>();
|
||||
private Map<String, ObservableResult> idToResult = new HashMap<>();
|
||||
|
||||
private List<EvalRegistryObj.RegistryFileInfo> registryFileData = null;
|
||||
|
||||
private final boolean skipShortCircuit = true;
|
||||
|
||||
// Hidden constructor for the report
|
||||
private STIXReportModule() {
|
||||
}
|
||||
|
||||
// Get the default implementation of this report
|
||||
public static synchronized STIXReportModule getDefault() {
|
||||
if (instance == null) {
|
||||
instance = new STIXReportModule();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param settings Report settings.
|
||||
* @param progressPanel panel to update the report's progress
|
||||
*/
|
||||
@Override
|
||||
@Messages({"STIXReportModule.srcModuleName.text=STIX Report"})
|
||||
public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
|
||||
// Start the progress bar and setup the report
|
||||
progressPanel.setIndeterminate(false);
|
||||
progressPanel.start();
|
||||
String baseReportDir = settings.getReportDirectoryPath();
|
||||
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.readSTIX"));
|
||||
reportPath = baseReportDir + getRelativeFilePath();
|
||||
File reportFile = new File(reportPath);
|
||||
// Check if the user wants to display all output or just hits
|
||||
reportAllResults = configPanel.getShowAllResults();
|
||||
|
||||
// Keep track of whether any errors occur during processing
|
||||
boolean hadErrors = false;
|
||||
|
||||
// Process the file/directory name entry
|
||||
String stixFileName = configPanel.getStixFile();
|
||||
|
||||
if (stixFileName == null) {
|
||||
logger.log(Level.SEVERE, "STIXReportModuleConfigPanel.stixFile not initialized "); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR, NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.noFildDirProvided"));
|
||||
new File(baseReportDir).delete();
|
||||
return;
|
||||
}
|
||||
if (stixFileName.isEmpty()) {
|
||||
logger.log(Level.SEVERE, "No STIX file/directory provided "); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR, NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.noFildDirProvided"));
|
||||
new File(baseReportDir).delete();
|
||||
return;
|
||||
}
|
||||
File stixFile = new File(stixFileName);
|
||||
|
||||
if (!stixFile.exists()) {
|
||||
logger.log(Level.SEVERE, String.format("Unable to open STIX file/directory %s", stixFileName)); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR, NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.couldNotOpenFileDir", stixFileName));
|
||||
new File(baseReportDir).delete();
|
||||
return;
|
||||
}
|
||||
|
||||
try (BufferedWriter output = new BufferedWriter(new FileWriter(reportFile))) {
|
||||
|
||||
// Create array of stix file(s)
|
||||
File[] stixFiles;
|
||||
if (stixFile.isFile()) {
|
||||
stixFiles = new File[1];
|
||||
stixFiles[0] = stixFile;
|
||||
} else {
|
||||
stixFiles = stixFile.listFiles();
|
||||
}
|
||||
|
||||
// Set the length of the progress bar - we increment twice for each file
|
||||
progressPanel.setMaximumProgress(stixFiles.length * 2 + 1);
|
||||
|
||||
// Process each STIX file
|
||||
for (File file : stixFiles) {
|
||||
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
processFile(file.getAbsolutePath(), progressPanel, output);
|
||||
} catch (TskCoreException | JAXBException ex) {
|
||||
String errMsg = String.format("Unable to process STIX file %s", file);
|
||||
logger.log(Level.SEVERE, errMsg, ex); //NON-NLS
|
||||
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), errMsg));
|
||||
hadErrors = true;
|
||||
break;
|
||||
}
|
||||
// Clear out the ID maps before loading the next file
|
||||
idToObjectMap = new HashMap<>();
|
||||
idToResult = new HashMap<>();
|
||||
}
|
||||
|
||||
// Set the progress bar to done. If any errors occurred along the way, modify
|
||||
// the "complete" message to indicate this.
|
||||
Case.getCurrentCaseThrows().addReport(reportPath, Bundle.STIXReportModule_srcModuleName_text(), "");
|
||||
if (hadErrors) {
|
||||
progressPanel.complete(ReportStatus.ERROR, NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.completedWithErrors"));
|
||||
} else {
|
||||
progressPanel.complete(ReportStatus.COMPLETE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to complete STIX report.", ex); //NON-NLS
|
||||
progressPanel.complete(ReportStatus.ERROR, NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.completedWithErrors"));
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to add report to database.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a STIX file.
|
||||
*
|
||||
* @param stixFile - Name of the file
|
||||
* @param progressPanel - Progress panel (for updating)
|
||||
* @param output
|
||||
*
|
||||
* @throws JAXBException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void processFile(String stixFile, ReportProgressPanel progressPanel, BufferedWriter output) throws
|
||||
JAXBException, TskCoreException {
|
||||
|
||||
// Load the STIX file
|
||||
STIXPackage stix;
|
||||
stix = loadSTIXFile(stixFile);
|
||||
|
||||
printFileHeader(stixFile, output);
|
||||
|
||||
// Save any observables listed up front
|
||||
processObservables(stix);
|
||||
progressPanel.increment();
|
||||
|
||||
// Make copies of the registry files
|
||||
registryFileData = EvalRegistryObj.copyRegistryFiles();
|
||||
|
||||
// Process the indicators
|
||||
processIndicators(stix, output, progressPanel);
|
||||
progressPanel.increment();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a STIX-formatted XML file into a STIXPackage object.
|
||||
*
|
||||
* @param stixFileName Name of the STIX file to unmarshal
|
||||
*
|
||||
* @return Unmarshalled file contents
|
||||
*
|
||||
* @throws JAXBException
|
||||
*/
|
||||
private STIXPackage loadSTIXFile(String stixFileName) throws JAXBException {
|
||||
// Create STIXPackage object from xml.
|
||||
// See JIRA-6958 for details about class loading and jaxb.
|
||||
ClassLoader original = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(STIXReportModule.class.getClassLoader());
|
||||
File file = new File(stixFileName);
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance("org.mitre.stix.stix_1:org.mitre.stix.common_1:org.mitre.stix.indicator_2:" //NON-NLS
|
||||
+ "org.mitre.cybox.objects:org.mitre.cybox.cybox_2:org.mitre.cybox.common_2"); //NON-NLS
|
||||
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
|
||||
STIXPackage stix = (STIXPackage) jaxbUnmarshaller.unmarshal(file);
|
||||
return stix;
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(original);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the initial processing of the list of observables. For each
|
||||
* observable, save it in a map using the ID as key.
|
||||
*
|
||||
* @param stix STIXPackage
|
||||
*/
|
||||
private void processObservables(STIXPackage stix) {
|
||||
if (stix.getObservables() != null) {
|
||||
List<Observable> obs = stix.getObservables().getObservables();
|
||||
for (Observable o : obs) {
|
||||
if (o.getId() != null) {
|
||||
saveToObjectMap(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all STIX indicators and save results to output file and create
|
||||
* artifacts.
|
||||
*
|
||||
* @param stix STIXPackage
|
||||
* @param output
|
||||
* @param progressPanel
|
||||
*/
|
||||
private void processIndicators(STIXPackage stix, BufferedWriter output, ReportProgressPanel progressPanel) throws TskCoreException {
|
||||
if (stix.getIndicators() != null) {
|
||||
List<IndicatorBaseType> s = stix.getIndicators().getIndicators();
|
||||
for (IndicatorBaseType t : s) {
|
||||
if (t instanceof Indicator) {
|
||||
Indicator ind = (Indicator) t;
|
||||
if (ind.getObservable() != null) {
|
||||
if (ind.getObservable().getObject() != null) {
|
||||
ObservableResult result = evaluateSingleObservable(ind.getObservable(), "");
|
||||
if (result.isTrue() || reportAllResults) {
|
||||
writeResultsToFile(ind, result.getDescription(), result.isTrue(), output);
|
||||
}
|
||||
if (result.isTrue()) {
|
||||
saveResultsAsArtifacts(ind, result, progressPanel);
|
||||
}
|
||||
} else if (ind.getObservable().getObservableComposition() != null) {
|
||||
ObservableResult result = evaluateObservableComposition(ind.getObservable().getObservableComposition(), " ");
|
||||
|
||||
if (result.isTrue() || reportAllResults) {
|
||||
writeResultsToFile(ind, result.getDescription(), result.isTrue(), output);
|
||||
}
|
||||
if (result.isTrue()) {
|
||||
saveResultsAsArtifacts(ind, result, progressPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the artifacts saved in the observable result.
|
||||
*
|
||||
* @param ind
|
||||
* @param result
|
||||
* @param progressPanel
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void saveResultsAsArtifacts(Indicator ind, ObservableResult result, ReportProgressPanel progressPanel) throws TskCoreException {
|
||||
|
||||
if (result.getArtifacts() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count of how many artifacts have been created for this indicator.
|
||||
int count = 0;
|
||||
|
||||
for (StixArtifactData s : result.getArtifacts()) {
|
||||
|
||||
// Figure out what name to use for this indicator. If it has a title,
|
||||
// use that. Otherwise use the ID. If both are missing, use a
|
||||
// generic heading.
|
||||
if (ind.getTitle() != null) {
|
||||
s.createArtifact(ind.getTitle());
|
||||
} else if (ind.getId() != null) {
|
||||
s.createArtifact(ind.getId().toString());
|
||||
} else {
|
||||
s.createArtifact("Unnamed indicator(s)"); //NON-NLS
|
||||
}
|
||||
|
||||
// Trying to protect against the case where we end up with tons of artifacts
|
||||
// for a single observable because the condition was not restrictive enough
|
||||
count++;
|
||||
if (count > 1000) {
|
||||
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(),
|
||||
"STIXReportModule.notifyMsg.tooManyArtifactsgt1000"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the full results string to the output file.
|
||||
*
|
||||
* @param ind - Used to get the title, ID, and description of the
|
||||
* indicator
|
||||
* @param resultStr - Full results for this indicator
|
||||
* @param found - true if the indicator was found in datasource(s)
|
||||
* @param output
|
||||
*/
|
||||
private void writeResultsToFile(Indicator ind, String resultStr, boolean found, BufferedWriter output) {
|
||||
if (output != null) {
|
||||
try {
|
||||
if (found) {
|
||||
output.write("----------------\r\n"
|
||||
+ "Found indicator:\r\n"); //NON-NLS
|
||||
} else {
|
||||
output.write("-----------------------\r\n"
|
||||
+ "Did not find indicator:\r\n"); //NON-NLS
|
||||
}
|
||||
if (ind.getTitle() != null) {
|
||||
output.write("Title: " + ind.getTitle() + "\r\n"); //NON-NLS
|
||||
} else {
|
||||
output.write("\r\n");
|
||||
}
|
||||
if (ind.getId() != null) {
|
||||
output.write("ID: " + ind.getId() + "\r\n"); //NON-NLS
|
||||
}
|
||||
|
||||
if (ind.getDescription() != null) {
|
||||
String desc = ind.getDescription().getValue();
|
||||
desc = desc.trim();
|
||||
output.write("Description: " + desc + "\r\n"); //NON-NLS
|
||||
}
|
||||
output.write("\r\nObservable results:\r\n" + resultStr + "\r\n\r\n"); //NON-NLS
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error writing to STIX report file %s", reportPath), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the a header for the current file to the output file.
|
||||
*
|
||||
* @param a_fileName
|
||||
* @param output
|
||||
*/
|
||||
private void printFileHeader(String a_fileName, BufferedWriter output) {
|
||||
if (output != null) {
|
||||
try {
|
||||
char[] chars = new char[a_fileName.length() + 8];
|
||||
Arrays.fill(chars, '#');
|
||||
String header = new String(chars);
|
||||
output.write("\r\n" + header);
|
||||
output.write("\r\n");
|
||||
output.write("### " + a_fileName + " ###\r\n");
|
||||
output.write(header + "\r\n\r\n");
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error writing to STIX report file %s", reportPath), ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the ID or ID ref to create a key into the observable map.
|
||||
*
|
||||
* @param obs
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String makeMapKey(Observable obs) {
|
||||
QName idQ;
|
||||
if (obs.getId() != null) {
|
||||
idQ = obs.getId();
|
||||
} else if (obs.getIdref() != null) {
|
||||
idQ = obs.getIdref();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return idQ.getLocalPart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an observable in the object map.
|
||||
*
|
||||
* @param obs
|
||||
*/
|
||||
private void saveToObjectMap(Observable obs) {
|
||||
|
||||
if (obs.getObject() != null) {
|
||||
idToObjectMap.put(makeMapKey(obs), obs.getObject());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an observable composition. Can be called recursively.
|
||||
*
|
||||
* @param comp The observable composition object to evaluate
|
||||
* @param spacing Used to formatting the output
|
||||
*
|
||||
* @return The status of the composition
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private ObservableResult evaluateObservableComposition(ObservableCompositionType comp, String spacing) throws TskCoreException {
|
||||
if (comp.getOperator() == null) {
|
||||
throw new TskCoreException("No operator found in composition"); //NON-NLS
|
||||
}
|
||||
|
||||
if (comp.getObservables() != null) {
|
||||
List<Observable> obsList = comp.getObservables();
|
||||
|
||||
// Split based on the type of composition (AND vs OR)
|
||||
if (comp.getOperator() == OperatorTypeEnum.AND) {
|
||||
ObservableResult result = new ObservableResult(OperatorTypeEnum.AND, spacing);
|
||||
for (Observable o : obsList) {
|
||||
|
||||
ObservableResult newResult; // The combined result for the composition
|
||||
if (o.getObservableComposition() != null) {
|
||||
newResult = evaluateObservableComposition(o.getObservableComposition(), spacing + " ");
|
||||
if (result == null) {
|
||||
result = newResult;
|
||||
} else {
|
||||
result.addResult(newResult, OperatorTypeEnum.AND);
|
||||
}
|
||||
} else {
|
||||
newResult = evaluateSingleObservable(o, spacing + " ");
|
||||
if (result == null) {
|
||||
result = newResult;
|
||||
} else {
|
||||
result.addResult(newResult, OperatorTypeEnum.AND);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!skipShortCircuit) && !result.isFalse()) {
|
||||
// For testing purposes (and maybe in general), may not want to short-circuit
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// At this point, all comparisions should have been true (or indeterminate)
|
||||
if (result == null) {
|
||||
// This really shouldn't happen, but if we have an empty composition,
|
||||
// indeterminate seems like a reasonable result
|
||||
return new ObservableResult("", "", spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} else {
|
||||
ObservableResult result = new ObservableResult(OperatorTypeEnum.OR, spacing);
|
||||
for (Observable o : obsList) {
|
||||
|
||||
ObservableResult newResult;// The combined result for the composition
|
||||
|
||||
if (o.getObservableComposition() != null) {
|
||||
newResult = evaluateObservableComposition(o.getObservableComposition(), spacing + " ");
|
||||
if (result == null) {
|
||||
result = newResult;
|
||||
} else {
|
||||
result.addResult(newResult, OperatorTypeEnum.OR);
|
||||
}
|
||||
} else {
|
||||
newResult = evaluateSingleObservable(o, spacing + " ");
|
||||
if (result == null) {
|
||||
result = newResult;
|
||||
} else {
|
||||
result.addResult(newResult, OperatorTypeEnum.OR);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!skipShortCircuit) && result.isTrue()) {
|
||||
// For testing (and maybe in general), may not want to short-circuit
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// At this point, all comparisions were false (or indeterminate)
|
||||
if (result == null) {
|
||||
// This really shouldn't happen, but if we have an empty composition,
|
||||
// indeterminate seems like a reasonable result
|
||||
return new ObservableResult("", "", spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
throw new TskCoreException("No observables found in list"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate one observable and return the result. This is at the end of the
|
||||
* observable composition tree and will not be called recursively.
|
||||
*
|
||||
* @param obs The observable object to evaluate
|
||||
* @param spacing For formatting the output
|
||||
*
|
||||
* @return The status of the observable
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private ObservableResult evaluateSingleObservable(Observable obs, String spacing) throws TskCoreException {
|
||||
|
||||
// If we've already calculated this one, return the saved value
|
||||
if (idToResult.containsKey(makeMapKey(obs))) {
|
||||
return idToResult.get(makeMapKey(obs));
|
||||
}
|
||||
|
||||
if (obs.getIdref() == null) {
|
||||
|
||||
// We should have the object data right here (as opposed to elsewhere in the STIX file).
|
||||
// Save it to the map.
|
||||
if (obs.getId() != null) {
|
||||
saveToObjectMap(obs);
|
||||
}
|
||||
|
||||
if (obs.getObject() != null) {
|
||||
|
||||
ObservableResult result = evaluateObject(obs.getObject(), spacing, makeMapKey(obs));
|
||||
idToResult.put(makeMapKey(obs), result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (idToObjectMap.containsKey(makeMapKey(obs))) {
|
||||
ObservableResult result = evaluateObject(idToObjectMap.get(makeMapKey(obs)), spacing, makeMapKey(obs));
|
||||
idToResult.put(makeMapKey(obs), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new TskCoreException("Error loading/finding object for observable " + obs.getIdref()); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a STIX object.
|
||||
*
|
||||
*
|
||||
* @param obj The object to evaluate against the datasource(s)
|
||||
* @param spacing For formatting the output
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ObservableResult evaluateObject(ObjectType obj, String spacing, String id) {
|
||||
|
||||
EvaluatableObject evalObj;
|
||||
|
||||
if (obj.getProperties() instanceof FileObjectType) {
|
||||
evalObj = new EvalFileObj((FileObjectType) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof Address) {
|
||||
evalObj = new EvalAddressObj((Address) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof URIObjectType) {
|
||||
evalObj = new EvalURIObj((URIObjectType) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof EmailMessage) {
|
||||
evalObj = new EvalEmailObj((EmailMessage) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof WindowsNetworkShare) {
|
||||
evalObj = new EvalNetworkShareObj((WindowsNetworkShare) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof AccountObjectType) {
|
||||
evalObj = new EvalAccountObj((AccountObjectType) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof SystemObjectType) {
|
||||
evalObj = new EvalSystemObj((SystemObjectType) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof URLHistory) {
|
||||
evalObj = new EvalURLHistoryObj((URLHistory) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof DomainName) {
|
||||
evalObj = new EvalDomainObj((DomainName) obj.getProperties(), id, spacing);
|
||||
} else if (obj.getProperties() instanceof WindowsRegistryKey) {
|
||||
evalObj = new EvalRegistryObj((WindowsRegistryKey) obj.getProperties(), id, spacing, registryFileData);
|
||||
} else {
|
||||
// Try to get the object type as a string
|
||||
String type = obj.getProperties().toString();
|
||||
type = type.substring(0, type.indexOf("@"));
|
||||
if ((type.lastIndexOf(".") + 1) < type.length()) {
|
||||
type = type.substring(type.lastIndexOf(".") + 1);
|
||||
}
|
||||
return new ObservableResult(id, type + " not supported", //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.INDETERMINATE, null);
|
||||
}
|
||||
|
||||
// Evalutate the object
|
||||
return evalObj.evaluate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
String name = NbBundle.getMessage(this.getClass(), "STIXReportModule.getName.text");
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativeFilePath() {
|
||||
return "stix.txt"; //NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
String desc = NbBundle.getMessage(this.getClass(), "STIXReportModule.getDesc.text");
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getConfigurationPanel() {
|
||||
initializePanel();
|
||||
return configPanel;
|
||||
}
|
||||
|
||||
private void initializePanel() {
|
||||
if (configPanel == null) {
|
||||
configPanel = new STIXReportModuleConfigPanel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default configuration for this report module.
|
||||
*
|
||||
* @return Object which contains default report module settings.
|
||||
*/
|
||||
@Override
|
||||
public ReportModuleSettings getDefaultConfiguration() {
|
||||
return new STIXReportModuleSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current configuration for this report module.
|
||||
*
|
||||
* @return Object which contains current report module settings.
|
||||
*/
|
||||
@Override
|
||||
public ReportModuleSettings getConfiguration() {
|
||||
initializePanel();
|
||||
return configPanel.getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set report module configuration.
|
||||
*
|
||||
* @param settings Object which contains report module settings.
|
||||
*/
|
||||
@Override
|
||||
public void setConfiguration(ReportModuleSettings settings) {
|
||||
initializePanel();
|
||||
if (settings == null || settings instanceof NoReportModuleSettings) {
|
||||
configPanel.setConfiguration((STIXReportModuleSettings) getDefaultConfiguration());
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings instanceof STIXReportModuleSettings) {
|
||||
configPanel.setConfiguration((STIXReportModuleSettings) settings);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected settings argument to be an instance of STIXReportModuleSettings");
|
||||
}
|
||||
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jStixFileTextField" min="-2" pref="292" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jButton1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jCheckBox1" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="73" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="jStixFileTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="225" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="jLabel2">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/modules/stix/Bundle.properties" key="STIXReportModuleConfigPanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="jStixFileTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/modules/stix/Bundle.properties" key="STIXReportModuleConfigPanel.jStixFileTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jStixFileTextFieldActionPerformed"/>
|
||||
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="jStixFileTextFieldKeyReleased"/>
|
||||
<EventHandler event="keyTyped" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="jStixFileTextFieldKeyTyped"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="jButton1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/modules/stix/Bundle.properties" key="STIXReportModuleConfigPanel.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="jCheckBox1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/modules/stix/Bundle.properties" key="STIXReportModuleConfigPanel.jCheckBox1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jCheckBox1ActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.io.File;
|
||||
import javax.swing.JFileChooser;
|
||||
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
|
||||
|
||||
/**
|
||||
* Configuration panel for STIX report generation.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class STIXReportModuleConfigPanel extends javax.swing.JPanel {
|
||||
|
||||
String stixFile = null;
|
||||
boolean showAllResults;
|
||||
private final JFileChooserFactory chooserHelper;
|
||||
|
||||
/**
|
||||
* Creates new form STIXReportModuleConfigPanel
|
||||
*/
|
||||
public STIXReportModuleConfigPanel() {
|
||||
initComponents();
|
||||
showAllResults = false;
|
||||
jCheckBox1.setSelected(false);
|
||||
chooserHelper = new JFileChooserFactory();
|
||||
}
|
||||
|
||||
void setConfiguration(STIXReportModuleSettings settings) {
|
||||
jStixFileTextField.setText(settings.getStixFile());
|
||||
showAllResults = settings.isShowAllResults();
|
||||
jCheckBox1.setSelected(settings.isShowAllResults());
|
||||
}
|
||||
|
||||
STIXReportModuleSettings getConfiguration() {
|
||||
return new STIXReportModuleSettings(jStixFileTextField.getText(), jCheckBox1.isSelected());
|
||||
}
|
||||
|
||||
String getStixFile() {
|
||||
return stixFile;
|
||||
}
|
||||
|
||||
boolean getShowAllResults() {
|
||||
return showAllResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
jStixFileTextField = new javax.swing.JTextField();
|
||||
jButton1 = new javax.swing.JButton();
|
||||
jCheckBox1 = new javax.swing.JCheckBox();
|
||||
|
||||
jLabel2.setText(org.openide.util.NbBundle.getMessage(STIXReportModuleConfigPanel.class, "STIXReportModuleConfigPanel.jLabel2.text")); // NOI18N
|
||||
|
||||
jStixFileTextField.setText(org.openide.util.NbBundle.getMessage(STIXReportModuleConfigPanel.class, "STIXReportModuleConfigPanel.jStixFileTextField.text")); // NOI18N
|
||||
jStixFileTextField.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jStixFileTextFieldActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jStixFileTextField.addKeyListener(new java.awt.event.KeyAdapter() {
|
||||
public void keyReleased(java.awt.event.KeyEvent evt) {
|
||||
jStixFileTextFieldKeyReleased(evt);
|
||||
}
|
||||
public void keyTyped(java.awt.event.KeyEvent evt) {
|
||||
jStixFileTextFieldKeyTyped(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jButton1.setText(org.openide.util.NbBundle.getMessage(STIXReportModuleConfigPanel.class, "STIXReportModuleConfigPanel.jButton1.text")); // NOI18N
|
||||
jButton1.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButton1ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jCheckBox1.setText(org.openide.util.NbBundle.getMessage(STIXReportModuleConfigPanel.class, "STIXReportModuleConfigPanel.jCheckBox1.text")); // NOI18N
|
||||
jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jCheckBox1ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel2)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jStixFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jButton1))
|
||||
.addComponent(jCheckBox1))
|
||||
.addContainerGap(73, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jLabel2)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jStixFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jButton1))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(jCheckBox1)
|
||||
.addContainerGap(225, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void jStixFileTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jStixFileTextFieldActionPerformed
|
||||
|
||||
}//GEN-LAST:event_jStixFileTextFieldActionPerformed
|
||||
|
||||
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
|
||||
|
||||
JFileChooser fileChooser = chooserHelper.getChooser();
|
||||
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
||||
|
||||
File currentSelection = new File(jStixFileTextField.getText());
|
||||
if (currentSelection.exists()) {
|
||||
fileChooser.setCurrentDirectory(currentSelection);
|
||||
}
|
||||
|
||||
int result = fileChooser.showOpenDialog(this);
|
||||
|
||||
if (result == JFileChooser.APPROVE_OPTION) {
|
||||
stixFile = fileChooser.getSelectedFile().getAbsolutePath();
|
||||
jStixFileTextField.setText(stixFile);
|
||||
}
|
||||
|
||||
}//GEN-LAST:event_jButton1ActionPerformed
|
||||
|
||||
private void jStixFileTextFieldKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jStixFileTextFieldKeyTyped
|
||||
|
||||
}//GEN-LAST:event_jStixFileTextFieldKeyTyped
|
||||
|
||||
private void jStixFileTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jStixFileTextFieldKeyReleased
|
||||
stixFile = jStixFileTextField.getText();
|
||||
}//GEN-LAST:event_jStixFileTextFieldKeyReleased
|
||||
|
||||
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox1ActionPerformed
|
||||
showAllResults = jCheckBox1.isSelected();
|
||||
}//GEN-LAST:event_jCheckBox1ActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton jButton1;
|
||||
private javax.swing.JCheckBox jCheckBox1;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JTextField jStixFileTextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import org.sleuthkit.autopsy.report.ReportModuleSettings;
|
||||
|
||||
/**
|
||||
* Settings for the STIX report module.
|
||||
*/
|
||||
class STIXReportModuleSettings implements ReportModuleSettings {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String stixFile;
|
||||
private final boolean showAllResults;
|
||||
|
||||
STIXReportModuleSettings() {
|
||||
stixFile = null;
|
||||
showAllResults = false;
|
||||
}
|
||||
|
||||
STIXReportModuleSettings(String stixFile, boolean showAllResults) {
|
||||
this.stixFile = stixFile;
|
||||
this.showAllResults = showAllResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersionNumber() {
|
||||
return serialVersionUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the stixFile
|
||||
*/
|
||||
String getStixFile() {
|
||||
return stixFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the showAllResults
|
||||
*/
|
||||
boolean isShowAllResults() {
|
||||
return showAllResults;
|
||||
}
|
||||
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.report.modules.stix;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
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 org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class StixArtifactData {
|
||||
private static final String MODULE_NAME = "Stix";
|
||||
|
||||
private AbstractFile file;
|
||||
private final String observableId;
|
||||
private final String objType;
|
||||
private static final Logger logger = Logger.getLogger(StixArtifactData.class.getName());
|
||||
|
||||
StixArtifactData(AbstractFile a_file, String a_observableId, String a_objType) {
|
||||
file = a_file;
|
||||
observableId = a_observableId;
|
||||
objType = a_objType;
|
||||
}
|
||||
|
||||
StixArtifactData(long a_objId, String a_observableId, String a_objType) {
|
||||
try {
|
||||
Case case1 = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
|
||||
file = sleuthkitCase.getAbstractFileById(a_objId);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
file = null;
|
||||
}
|
||||
observableId = a_observableId;
|
||||
objType = a_objType;
|
||||
}
|
||||
|
||||
@Messages({"StixArtifactData.indexError.message=Failed to index STIX interesting file hit artifact for keyword search.",
|
||||
"StixArtifactData.noOpenCase.errMsg=No open case available."})
|
||||
void createArtifact(String a_title) throws TskCoreException {
|
||||
Blackboard blackboard;
|
||||
try {
|
||||
blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
String setName = "STIX Indicator - " + StringUtils.defaultIfBlank(a_title, "(no title)"); //NON-NLS
|
||||
|
||||
Collection<BlackboardAttribute> attributes = Arrays.asList(
|
||||
new BlackboardAttribute(TSK_SET_NAME, MODULE_NAME, setName),
|
||||
new BlackboardAttribute(TSK_TITLE, MODULE_NAME, observableId),
|
||||
new BlackboardAttribute(TSK_CATEGORY, MODULE_NAME, objType));
|
||||
|
||||
// Create artifact if it doesn't already exist.
|
||||
if (!blackboard.artifactExists(file, TSK_INTERESTING_FILE_HIT, attributes)) {
|
||||
BlackboardArtifact bba = file.newAnalysisResult(
|
||||
BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, Score.SCORE_LIKELY_NOTABLE,
|
||||
null, setName, null,
|
||||
attributes)
|
||||
.getAnalysisResult();
|
||||
|
||||
try {
|
||||
/*
|
||||
* post the artifact which will index the artifact for keyword
|
||||
* search, and fire an event to notify UI of this new artifact
|
||||
*/
|
||||
blackboard.postArtifact(bba, MODULE_NAME);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,9 +40,22 @@ public final class ViewArtifactInTimelineAction extends AbstractAction {
|
||||
|
||||
private final BlackboardArtifact artifact;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param artifact The artifact to navigate to in the timeline.
|
||||
*/
|
||||
@NbBundle.Messages({"ViewArtifactInTimelineAction.displayName=View Result in Timeline... "})
|
||||
public ViewArtifactInTimelineAction(BlackboardArtifact artifact) {
|
||||
super(Bundle.ViewArtifactInTimelineAction_displayName());
|
||||
this(artifact, Bundle.ViewArtifactInTimelineAction_displayName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param artifact The artifact to navigate to in the timeline.
|
||||
* @param displayName The display name for the action.
|
||||
*/
|
||||
public ViewArtifactInTimelineAction(BlackboardArtifact artifact, String displayName) {
|
||||
super(displayName);
|
||||
this.artifact = artifact;
|
||||
// If timeline functionality is not available this action is disabled.
|
||||
if ("false".equals(ModuleSettings.getConfigSetting("timeline", "enable_timeline"))) {
|
||||
|
@ -43,7 +43,12 @@ public final class ViewFileInTimelineAction extends AbstractAction {
|
||||
|
||||
private final AbstractFile file;
|
||||
|
||||
private ViewFileInTimelineAction(AbstractFile file, String displayName) {
|
||||
/**
|
||||
* Constructor.
|
||||
* @param file The file to view in the timeline.
|
||||
* @param displayName The display name of the action.
|
||||
*/
|
||||
public ViewFileInTimelineAction(AbstractFile file, String displayName) {
|
||||
super(displayName);
|
||||
this.file = file;
|
||||
|
||||
|
@ -384,6 +384,20 @@ final class RegexQuery implements KeywordSearchQuery {
|
||||
}
|
||||
// Replace all non numeric at the end of the hit.
|
||||
hit = hit.replaceAll("[^0-9]$", "");
|
||||
|
||||
if (offset > 1) {
|
||||
/*
|
||||
* NOTE: our IP and phone number regex patterns look for
|
||||
* boundary characters immediately before and after
|
||||
* the keyword hit. After a match, Java pattern
|
||||
* mather re-starts at the first character not
|
||||
* matched by the previous match. This basically
|
||||
* requires two boundary characters to be present
|
||||
* between each pattern match. To mitigate this we
|
||||
* are resetting the offest one character back.
|
||||
*/
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an
|
||||
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
|
||||
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
|
||||
ChromeCacheExtractor.moduleName=ChromeCacheExtractor
|
||||
# {0} - module name
|
||||
# {1} - row number
|
||||
# {2} - table length
|
||||
# {3} - cache path
|
||||
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
|
||||
DataSourceUsage_AndroidMedia=Android Media Card
|
||||
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
|
||||
@ -152,13 +156,19 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{
|
||||
Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts.
|
||||
Progress_Message_Analyze_Registry=Analyzing Registry Files
|
||||
Progress_Message_Analyze_Usage=Data Sources Usage Analysis
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}
|
||||
Progress_Message_Chrome_Cache=Chrome Cache
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}
|
||||
Progress_Message_Chrome_FormHistory=Chrome Form History
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_History=Chrome History Browser {0}
|
||||
# {0} - browserName
|
||||
Progress_Message_Chrome_Logins=Chrome Logins Browser {0}
|
||||
Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks
|
||||
Progress_Message_Edge_Cookies=Microsoft Edge Cookies
|
||||
|
@ -592,8 +592,13 @@ final class ChromeCacheExtractor {
|
||||
|
||||
// see if it is cached
|
||||
String fileTableKey = cacheFolderName + cacheFileName;
|
||||
if (cacheFileName.startsWith("f_") && externalFilesTable.containsKey(fileTableKey)) {
|
||||
return Optional.of(externalFilesTable.get(fileTableKey));
|
||||
|
||||
if (cacheFileName != null) {
|
||||
if (cacheFileName.startsWith("f_") && externalFilesTable.containsKey(fileTableKey)) {
|
||||
return Optional.of(externalFilesTable.get(fileTableKey));
|
||||
}
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (fileCopyCache.containsKey(fileTableKey)) {
|
||||
@ -1306,7 +1311,7 @@ final class ChromeCacheExtractor {
|
||||
|
||||
private String key; // Key may be found within the entry or may be external
|
||||
|
||||
CacheEntry(CacheAddress cacheAdress, FileWrapper cacheFileCopy ) throws TskCoreException {
|
||||
CacheEntry(CacheAddress cacheAdress, FileWrapper cacheFileCopy ) throws TskCoreException, IngestModuleException {
|
||||
this.selfAddress = cacheAdress;
|
||||
this.cacheFileCopy = cacheFileCopy;
|
||||
|
||||
@ -1315,7 +1320,11 @@ final class ChromeCacheExtractor {
|
||||
int entryOffset = DATAFILE_HDR_SIZE + cacheAdress.getStartBlock() * cacheAdress.getBlockSize();
|
||||
|
||||
// reposition the buffer to the the correct offset
|
||||
fileROBuf.position(entryOffset);
|
||||
if (entryOffset < fileROBuf.capacity()) {
|
||||
fileROBuf.position(entryOffset);
|
||||
} else {
|
||||
throw new IngestModuleException("Position seeked in Buffer to big"); // NON-NLS
|
||||
}
|
||||
|
||||
hash = fileROBuf.getInt() & UINT32_MASK;
|
||||
|
||||
@ -1364,11 +1373,13 @@ final class ChromeCacheExtractor {
|
||||
if (longKeyAddresses != null) {
|
||||
// Key is stored outside of the entry
|
||||
try {
|
||||
CacheDataSegment data = new CacheDataSegment(longKeyAddresses, this.keyLen, true);
|
||||
key = data.getDataString();
|
||||
if (longKeyAddresses.getFilename() != null) {
|
||||
CacheDataSegment data = new CacheDataSegment(longKeyAddresses, this.keyLen, true);
|
||||
key = data.getDataString();
|
||||
}
|
||||
} catch (TskCoreException | IngestModuleException ex) {
|
||||
throw new TskCoreException(String.format("Failed to get external key from address %s", longKeyAddresses)); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // key stored within entry
|
||||
StringBuilder strBuilder = new StringBuilder(MAX_KEY_LEN);
|
||||
|
@ -318,7 +318,13 @@ final class ExtractZoneIdentifier extends Extract {
|
||||
*/
|
||||
ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException {
|
||||
fileName = zoneFile.getName();
|
||||
properties.load(new ReadContentInputStream(zoneFile));
|
||||
// properties.load will throw IllegalArgument if unicode characters are found in the zone file.
|
||||
try {
|
||||
properties.load(new ReadContentInputStream(zoneFile));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String message = String.format("Unable to parse Zone Id for File %s", fileName); //NON-NLS
|
||||
LOG.log(Level.WARNING, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 24 KiB |
@ -71,7 +71,6 @@ The following topics are available here:
|
||||
- \subpage ui_quick_search
|
||||
- \subpage file_search_page
|
||||
- \subpage ad_hoc_keyword_search_page
|
||||
- \subpage stix_page
|
||||
- \subpage common_properties_page
|
||||
- \subpage search_all_cases_page
|
||||
|
||||
|
@ -101,11 +101,6 @@ This report module generates a KML file from any GPS data in the case. This file
|
||||
|
||||
This report module generates a new Autopsy case that includes tagged and/or interesting items. See the \ref portable_case_page page for additional information.
|
||||
|
||||
\subsection report_stix STIX
|
||||
|
||||
The STIX module allows you to generate a report and Interesting File artifacts by running a STIX file (or files) against the data sources in the case.
|
||||
For more information see the \ref stix_page page.
|
||||
|
||||
\subsection report_body_file TSK Body File
|
||||
|
||||
This module generates a <a href="https://wiki.sleuthkit.org/index.php?title=Body_file">TSK Body File</a> from the files in your case, which looks similar to the following:
|
||||
|
@ -1,104 +0,0 @@
|
||||
/*! \page stix_page STIX
|
||||
|
||||
[TOC]
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
This document outlines the use of the STIX feature of Autopsy. This feature allows one or more Structured Threat Information Exchange (STIX) files to be run against a data source, reporting which indicators were found in the data source. More information about STIX can be found at https://stix.mitre.org/.
|
||||
This document assumes basic familiarity with Autopsy.
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
-# Create a case as normal and add a disk image (or folder of files) as a data source. To get the most out of the STIX module, ensure that the following ingest modules are selected:
|
||||
- Recent Activity
|
||||
- Hash Lookup (Check box to calculate MD5 hashes even with no database selected)
|
||||
- File Type Identification
|
||||
- Keyword Search (URL, IP, and Email addresses)
|
||||
- Email Parser
|
||||
- Extension Mismatch Detector
|
||||
-# After the image has been added and ingest is complete, click the Report button then select STIX. Next choose either a single STIX file or a directory of STIX files to run against the image. It is possible to do this while ingest is running but the results will be incomplete.
|
||||
-# Once the STIX report module is complete, there will be two sets of results:
|
||||
- Entries will be created under Interesting Items in the Autopsy tree, under a subheading for each indicator.
|
||||
- A log of which indicators/observables were found is generated by the report module (Follow the link on the Report Generation Progress window)
|
||||
|
||||
|
||||
Supported CybOX Objects
|
||||
=======================
|
||||
|
||||
- Address Object
|
||||
- Address_Value
|
||||
|
||||
- Domain Name Object
|
||||
- Value
|
||||
|
||||
- Email Message Object
|
||||
- To
|
||||
- CC
|
||||
- From
|
||||
- Subject
|
||||
|
||||
- File Object
|
||||
- Size_In_Bytes
|
||||
- File_Name
|
||||
- File_Path
|
||||
- File_Extension
|
||||
- Modified_Time
|
||||
- Accessed_Time
|
||||
- Created_Time
|
||||
- Hashes (MD5 only)
|
||||
- File_Format
|
||||
- is_masqueraded
|
||||
|
||||
- URI Object
|
||||
- Value
|
||||
|
||||
- URL History Object
|
||||
- Browser_Information (Name)
|
||||
- URL
|
||||
- Hostname
|
||||
- Referrer_URL
|
||||
- Page_Title
|
||||
- User_Profile_Name
|
||||
|
||||
- User Account Object
|
||||
- Home_Directory
|
||||
- Username
|
||||
|
||||
- Win Executable File Object
|
||||
- Time_Date_Stamp
|
||||
|
||||
- Windows Network Share Object
|
||||
- Local_Path
|
||||
- Netname
|
||||
|
||||
- Win Registry Key Object
|
||||
- Key (Required)
|
||||
- Hive
|
||||
- Values
|
||||
|
||||
- System Object
|
||||
- Hostname
|
||||
- Processor_Architecture
|
||||
|
||||
- Win System Object
|
||||
- Product_ID
|
||||
- Product_Name
|
||||
- Registered_Owner
|
||||
- Registered_Organization
|
||||
- Windows_System_Directory
|
||||
- Windows_Temp_Directory
|
||||
|
||||
- Win User Account Object
|
||||
- SID
|
||||
|
||||
See http://cybox.mitre.org for more information on CybOX Objects.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
- As shown in the list above, not all CybOX objects/fields are currently supported. When an unsupported object/field is found in an observable, its status is set to "indeterminate" instead of true or false. These indeterminate fields will not change the result of the observable composition (i.e., if the rest is true, the overall result will stay as true).
|
||||
- Not all ConditionTypeEnum values are supported. It varies by field, but generally on String fields the following work: EQUALS, DOES_NOT_EQUAL, CONTAINS, DOES_NOT_CONTAIN, STARTS_WITH, ENDS_WITH. If a condition type is not supported there will be a warning in the log file.
|
||||
- Related objects are not processed
|
||||
|
||||
|
||||
*/
|