Update external results import code

This commit is contained in:
Richard Cordovano 2014-06-02 17:40:10 -04:00
parent 2c39db9591
commit 388dc636af
7 changed files with 319 additions and 327 deletions

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.coreutils;
/**
* Encapsulates an error message and an associated exception, if any.
*/
final public class ErrorInfo {
private final String errorSource;
private final String message;
private final Exception exception;
public ErrorInfo(String errorSource, String message) {
this.errorSource = errorSource;
this.message = message;
this.exception = null;
}
public ErrorInfo(String errorSource, String message, Exception exception) {
this.errorSource = errorSource;
this.message = message;
this.exception = exception;
}
public String getErrroSource() {
return this.errorSource;
}
public String getMessage() {
return this.message;
}
public boolean hasException() {
return exception != null;
}
public Exception getException() {
return this.exception;
}
}

View File

@ -44,11 +44,11 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.externalresults.ExternalResults;
import org.sleuthkit.autopsy.externalresults.ExternalResultsImporter;
import org.sleuthkit.autopsy.externalresults.ExternalResultsXML;
import org.sleuthkit.autopsy.externalresults.ExternalResultsXMLParser;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
@ -111,7 +111,12 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM
progressBar.progress(1);
// Import the results of the analysis.
ExternalResultsImporter.importResultsFromXML(dataSource, resultsFilePath);
ExternalResults results = new ExternalResultsXMLParser(dataSource, resultsFilePath).parse();
// RJCTODO: Get error messages
ExternalResultsImporter importer = new ExternalResultsImporter();
importer.importResults(results);
// RJCTODO: Get error messages
progressBar.progress(2);
} catch (TskCoreException | ParserConfigurationException | TransformerException | IOException ex) {
Logger logger = IngestServices.getInstance().getLogger(moduleName);
@ -164,18 +169,18 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(ExternalResultsXML.ROOT_ELEM.toString());
Element rootElement = doc.createElement(ExternalResultsXMLParser.TagNames.ROOT_ELEM.toString());
doc.appendChild(rootElement);
// Add an artifacts list element to the root element.
Element artifactsListElement = doc.createElement(ExternalResultsXML.ARTIFACTS_LIST_ELEM.toString());
Element artifactsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACTS_LIST_ELEM.toString());
rootElement.appendChild(artifactsListElement);
// Add an artifact element to the artifacts list element. A standard
// artifact type is used as the required type attribute of this
// artifact element.
Element artifactElement = doc.createElement(ExternalResultsXML.ARTIFACT_ELEM.toString());
artifactElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel());
Element artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString());
artifactElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel());
artifactsListElement.appendChild(artifactElement);
// Add an optional file element to the artifact element, and add a path
@ -183,61 +188,61 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM
// artifact is usually specified. If an artifact element has no file
// element, the data source is used as the default source file for the
// artifact.
Element fileElement = doc.createElement(ExternalResultsXML.SOURCE_FILE_ELEM.toString());
Element fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString());
artifactElement.appendChild(fileElement);
Element pathElement = doc.createElement(ExternalResultsXML.PATH_ELEM.toString());
Element pathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PATH_ELEM.toString());
pathElement.setTextContent(dataSourcePath);
fileElement.appendChild(pathElement);
// Add an artifact attribute element to the artifact element. A standard
// artifact attribute type is used as the required type XML attribute of
// the artifact attribute element.
Element artifactAttrElement = doc.createElement(ExternalResultsXML.ATTRIBUTE_ELEM.toString());
artifactAttrElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel());
Element artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString());
artifactAttrElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel());
artifactElement.appendChild(artifactAttrElement);
// Add the required value element to the artifact attribute element,
// with an optional type XML attribute of ExternalXML.VALUE_TYPE_TEXT,
// which is the default.
Element artifactAttributeValueElement = doc.createElement(ExternalResultsXML.VALUE_ELEM.toString());
Element artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString());
artifactAttributeValueElement.setTextContent("SampleInterestingFilesSet");
artifactAttrElement.appendChild(artifactAttributeValueElement);
// Add an optional source module element to the artifct attribute
// element.
Element artifactAttrSourceElement = doc.createElement(ExternalResultsXML.SOURCE_MODULE_ELEM.toString());
Element artifactAttrSourceElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString());
artifactAttrSourceElement.setTextContent(moduleName);
artifactAttrElement.appendChild(artifactAttrSourceElement);
// Add an artifact element with a user-defined type. No file element is
// added to the artifact element, so the data source will be used as the
// default for the source file.
artifactElement = doc.createElement(ExternalResultsXML.ARTIFACT_ELEM.toString());
artifactElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), "SampleArtifactType");
artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString());
artifactElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), "SampleArtifactType");
artifactsListElement.appendChild(artifactElement);
// Add artifact attribute elements with user-defined types to the
// artifact element, adding value elements of assorted types.
for (int i = 0; i < 4; ++i) {
artifactAttrElement = doc.createElement(ExternalResultsXML.ATTRIBUTE_ELEM.toString());
artifactAttrElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), "SampleArtifactAttributeType");
artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString());
artifactAttrElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), "SampleArtifactAttributeType");
artifactElement.appendChild(artifactAttrElement);
artifactAttributeValueElement = doc.createElement(ExternalResultsXML.VALUE_ELEM.toString());
artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString());
switch (i) {
case 0:
artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_TEXT.toString());
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_TEXT.toString());
artifactAttributeValueElement.setTextContent("One");
break;
case 1:
artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_INT32.toString());
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_INT32.toString());
artifactAttributeValueElement.setTextContent("2");
break;
case 2:
artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_INT64.toString());
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_INT64.toString());
artifactAttributeValueElement.setTextContent("3");
break;
case 3:
artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_DOUBLE.toString());
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_DOUBLE.toString());
artifactAttributeValueElement.setTextContent("4.0");
break;
}
@ -245,25 +250,25 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM
}
// Add a reports list element to the root element.
Element reportsListElement = doc.createElement(ExternalResultsXML.REPORTS_LIST_ELEM.toString());
Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString());
rootElement.appendChild(reportsListElement);
// Add a report element to the reports list element.
Element reportElement = doc.createElement(ExternalResultsXML.REPORT_ELEM.toString());
Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString());
reportsListElement.appendChild(reportElement);
// Add the required display name element to the report element.
Element reportDisplayNameElement = doc.createElement(ExternalResultsXML.DISPLAY_NAME_ELEM.toString());
Element reportDisplayNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.DISPLAY_NAME_ELEM.toString());
reportDisplayNameElement.setTextContent("Sample Report");
reportElement.appendChild(reportDisplayNameElement);
// Add the required local path element to the report element.
Element reportPathElement = doc.createElement(ExternalResultsXML.LOCAL_PATH_ELEM.toString());
Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString());
reportPathElement.setTextContent(reportPath);
reportElement.appendChild(reportPathElement);
// Add a derived files list element to the root element.
Element derivedFilesListElement = doc.createElement(ExternalResultsXML.DERIVED_FILES_LIST_ELEM.toString());
Element derivedFilesListElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILES_LIST_ELEM.toString());
rootElement.appendChild(derivedFilesListElement);
// Add derived file elements to the derived files list element. Each
@ -273,13 +278,13 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM
// parent.
Element parentPathElement = null;
for (String filePath : derivedFilePaths) {
Element derivedFileElement = doc.createElement(ExternalResultsXML.DERIVED_FILE_ELEM.toString());
Element derivedFileElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILE_ELEM.toString());
derivedFilesListElement.appendChild(derivedFileElement);
Element localPathElement = doc.createElement(ExternalResultsXML.LOCAL_PATH_ELEM.toString());
Element localPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString());
localPathElement.setTextContent(filePath);
derivedFileElement.appendChild(localPathElement);
if (parentPathElement == null) {
parentPathElement = doc.createElement(ExternalResultsXML.PARENT_PATH_ELEM.toString());
parentPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PARENT_PATH_ELEM.toString());
parentPathElement.setTextContent(dataSourcePath);
derivedFileElement.appendChild(parentPathElement);
}

View File

@ -32,7 +32,7 @@ final public class ExternalResults {
private final List<Artifact> artifacts = new ArrayList<>();
private final List<Report> reports = new ArrayList<>();
private final List<DerivedFile> derivedFiles = new ArrayList<>();
ExternalResults(Content dataSource) {
this.dataSource = dataSource;
}

View File

@ -23,10 +23,12 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
@ -36,232 +38,165 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Mechanism to import blackboard items, derived files, etc. It is decoupled
* from the actual parsing/interfacing with external data.
* Uses a standard representation of results data (e.g., artifacts, derived
* files, reports) to import results generated by a process external to Autopsy
* into Autopsy.
*/
public class ExternalResultsImporter {
public final class ExternalResultsImporter {
private static final Logger logger = Logger.getLogger(ExternalResultsImporter.class.getName());
private static final String EVENT_STRING = "External Results";
private final List<ErrorInfo> errors = new ArrayList<>();
/**
* Import results for a data source from an XML localFile (see
* org.sleuthkit.autopsy.externalresults.autopsy_external_results.xsd).
* Import results generated by a process external to Autopsy into Autopsy.
*
* @param dataSource A data source.
* @param resultsXmlPath Path to an XML localFile containing results (e.g.,
* blackboard artifacts, derived files, reports) from the data source.
* @param results A standard representation of results data (e.g.,
* artifacts, derived files, reports)from the data source.
* @return A collection of error messages, possibly empty. The error
* messages are already logged but are provided to allow the caller to
* provide additional user feedback via the Autopsy user interface.
*/
public static void importResultsFromXML(Content dataSource, String resultsXmlPath) { // RJCTODO: Return error messages
ExternalResults results = new ExternalResultsXMLParser(dataSource, resultsXmlPath).parse(); // RJCTODO: Return error messages
// RJCTODO: Get external results, restore ExternalResults parser interface
importDerivedFiles(results); // Import files first, they may be artifact sources.
importArtifacts(dataSource, results);
public List<ErrorInfo> importResults(ExternalResults results) {
// Import files first, they may be artifactData sources.
importDerivedFiles(results);
importArtifacts(results);
importReports(results);
//
return Collections.unmodifiableList(this.errors);
}
private static void importDerivedFiles(ExternalResults results) {
private void importDerivedFiles(ExternalResults results) {
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
for (ExternalResults.DerivedFile fileData : results.getDerivedFiles()) {
String filePath = Case.getCurrentCase().getCaseDirectory() + File.separator + fileData.getLocalPath();
try {
String localPath = fileData.getLocalPath();
File localFile = new File(localPath); // RJCTODO: State/check ExternalResults contract that is not empty
File localFile = new File(filePath);
if (!localFile.exists()) {
AbstractFile parentFile = findFileInDatabase(fileData.getParentPath());
if (parentFile == null) {
String relativePath = getPathRelativeToCaseFolder(fileData.getLocalPath());
DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(),
0, 0, 0, 0, // Do not currently have localFile times for derived files, should they provide via XML?
true, parentFile, "", "", "", "");
if (derivedFile != null) {
IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile));
}
AbstractFile parentFile = findFile(results.getDataSource(), fileData.getParentPath());
if (parentFile != null) {
DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), fileData.getLocalPath(), localFile.length(),
0, 0, 0, 0, // Do not currently have file times for derived files from external processes.
true, parentFile,
"", "", "", ""); // Not currently providing derivation info for derived files from external processes.
IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile));
} else {
// RJCTODO: parent file not in database
String errorMessage = String.format("Could not import derived file at %s, parent file %s not found", filePath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
}
} else {
// RJCTODO: file missing
String errorMessage = String.format("Could not import derived file at %s, file does not exist", filePath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage());
String errorMessage = String.format("Could not import derived file at %s, error querying/updating case database", filePath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
}
}
}
/**
* Create and add new blackboard artifacts, attributes, and types
*
* @param results
* @param dataSource
*/
private static void importArtifacts(Content dataSource, ExternalResults results) {
for (ExternalResults.Artifact art : results.getArtifacts()) {
private void importArtifacts(ExternalResults results) {
SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase();
for (ExternalResults.Artifact artifactData : results.getArtifacts()) {
try {
// Get the artifact type id if defined, or create a new
// user-defined artifact type.
int artifactTypeId;
BlackboardArtifact.ARTIFACT_TYPE stdArtType = isStandardArtifactType(art.getType());
if (stdArtType != null) {
artifactTypeId = stdArtType.getTypeID();
} else {
artifactTypeId = Case.getCurrentCase().getSleuthkitCase().addArtifactType(art.getType(), art.getType());
// Add the artifact to the case database.
boolean artifactTypeIsUserDefined = false;
int artifactTypeId = caseDb.getArtifactTypeIdIfExists(artifactData.getType());
if (artifactTypeId == -1) {
artifactTypeId = caseDb.addArtifactType(artifactData.getType(), artifactData.getType());
artifactTypeIsUserDefined = true;
}
Content sourceFile = findFile(results.getDataSource(), artifactData.getSourceFilePath());
BlackboardArtifact artifact = sourceFile.newArtifact(artifactTypeId);
Collection<BlackboardAttribute> bbAttributes = new ArrayList<>();
for (ExternalResults.ArtifactAttribute attr : art.getAttributes()) {
int bbAttrTypeId;
BlackboardAttribute.ATTRIBUTE_TYPE stdAttrType = isStandardAttributeType(attr.getType());
if (stdAttrType != null) {
bbAttrTypeId = stdAttrType.getTypeID();
} else {
// assume it's user defined RJCTODO fix
bbAttrTypeId = Case.getCurrentCase().getSleuthkitCase().addAttrType(attr.getType(), attr.getType());
// Add the artifact's attributes to the case database.
Collection<BlackboardAttribute> attributes = new ArrayList<>();
for (ExternalResults.ArtifactAttribute attributeData : artifactData.getAttributes()) {
int attributeTypeId = caseDb.getAttrTypeIdIfExists(attributeData.getType());
if (artifactTypeId == -1) {
artifactTypeId = caseDb.addArtifactType(attributeData.getType(), attributeData.getType());
}
BlackboardAttribute bbAttr = null;
switch (attr.getValueType()) {
switch (attributeData.getValueType()) {
case "text": //NON-NLS
bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), attr.getValue());
attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), attributeData.getValue()));
break;
case "int32": //NON-NLS
int intValue = Integer.parseInt(attr.getValue());
bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), intValue);
int intValue = Integer.parseInt(attributeData.getValue());
attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), intValue));
break;
case "int64": //NON-NLS
long longValue = Long.parseLong(attr.getValue());
bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), longValue);
long longValue = Long.parseLong(attributeData.getValue());
attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), longValue));
break;
case "double": //NON-NLS
double doubleValue = Double.parseDouble(attr.getValue());
bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), doubleValue);
double doubleValue = Double.parseDouble(attributeData.getValue());
attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), doubleValue));
break;
default:
logger.log(Level.WARNING, "Ignoring invalid attribute value type {0}", attr.getValueType());
String errorMessage = String.format("Could not import %s attribute, value = %s, for %s artifact from %s, unrecognized attribute value type: %s",
attributeData.getType(), attributeData.getValue(),
artifactData.getType(), artifactData.getSourceFilePath(),
attributeData.getValueType());
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
break;
}
if (bbAttr != null) {
bbAttributes.add(bbAttr);
}
}
artifact.addAttributes(attributes);
// Get associated localFile (if any) to use as the content obj to attach the artifact to
Content currContent = null;
if (art.getSourceFilePath().isEmpty()) {
currContent = findFileInDatabase(art.getSourceFilePath());
}
// If no associated localFile, use current data source itself
if (currContent == null) {
currContent = dataSource;
}
BlackboardArtifact bbArt = currContent.newArtifact(artifactTypeId);
bbArt.addAttributes(bbAttributes);
if (stdArtType != null) {
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("", stdArtType)); //NON-NLS
if (!artifactTypeIsUserDefined) {
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(this.getClass().getSimpleName(), BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactTypeId)));
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage());
String errorMessage = String.format("Could not import %s artifact from %s, error updating case database", artifactData.getType(), artifactData.getSourceFilePath());
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
}
}
}
private static void importReports(ExternalResults results) {
private void importReports(ExternalResults results) {
for (ExternalResults.Report report : results.getReports()) {
String reportPath = Case.getCurrentCase().getCaseDirectory() + File.separator + report.getLocalPath();
try {
String reportPath = report.getLocalPath();
File reportFile = new File(reportPath);
if (reportFile.exists()) {
// Try to get a relative local path
String relPath = reportPath;
Path pathTo = Paths.get(reportPath);
if (pathTo.isAbsolute()) {
Path pathBase = Paths.get(Case.getCurrentCase().getCaseDirectory());
try {
Path pathRelative = pathBase.relativize(pathTo);
relPath = pathRelative.toString();
} catch (IllegalArgumentException ex) {
logger.log(Level.WARNING, "Report file {0} path may be incorrect. The report record will still be added to the database.", reportPath);
}
}
if (!relPath.isEmpty()) {
Case.getCurrentCase().getSleuthkitCase().addReport(relPath, report.getDisplayName());
}
Case.getCurrentCase().getSleuthkitCase().addReport(reportPath, report.getDisplayName());
} else {
String errorMessage = String.format("Could not import report at %s, file does not exist", reportPath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage());
String errorMessage = String.format("Could not import report at %s, error updating case database", reportPath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
}
}
}
/**
* util function
*
* @param filePath full path including localFile or dir name
* @return AbstractFile
* @throws TskCoreException
*/
private static AbstractFile findFileInDatabase(String filePath) throws TskCoreException {
AbstractFile abstractFile = null;
String fileName = filePath;
String parentPath = "";
int charPos = filePath.lastIndexOf("/");
if (charPos >= 0) {
fileName = filePath.substring(charPos + 1);
parentPath = filePath.substring(0, charPos + 1);
}
String whereQuery = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS
List<AbstractFile> files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(whereQuery);
if (files.size() > 0) {
abstractFile = files.get(0);
if (files.size() > 1) {
logger.log(Level.WARNING, "Ignoring extra files found for path {0}", filePath);
}
}
return abstractFile;
}
private static String getPathRelativeToCaseFolder(String localPath) {
String relativePath = "";
Path path = Paths.get(localPath);
private AbstractFile findFile(Content dataSource, String filePath) throws TskCoreException {
AbstractFile file = null;
Path path = Paths.get(filePath);
if (path.isAbsolute()) {
Path pathBase = Paths.get(Case.getCurrentCase().getCaseDirectory());
try {
Path pathRelative = pathBase.relativize(path);
relativePath = pathRelative.toString();
} catch (IllegalArgumentException ex) {
// RJCTODO: Fix this
logger.log(Level.WARNING, "Report file {0} path may be incorrect. The report record will still be added to the database.", localPath);
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
List<AbstractFile> files = fileManager.openFiles(dataSource, filePath);
if (files.size() > 0) {
file = files.get(0);
if (files.size() > 1) {
String errorMessage = String.format("Ambiguous file path: %s", filePath);
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage));
}
}
} else {
// RJCTODO: Path is not absolute
// RJCTODO: Need a look up that goes for relative path
}
return relativePath;
}
private static BlackboardArtifact.ARTIFACT_TYPE isStandardArtifactType(String artTypeStr) {
BlackboardArtifact.ARTIFACT_TYPE[] stdArts = BlackboardArtifact.ARTIFACT_TYPE.values();
for (BlackboardArtifact.ARTIFACT_TYPE art : stdArts) {
if (art.getLabel().equals(artTypeStr)) {
return art;
}
}
return null;
}
private static BlackboardAttribute.ATTRIBUTE_TYPE isStandardAttributeType(String attrTypeStr) {
BlackboardAttribute.ATTRIBUTE_TYPE[] stdAttrs = BlackboardAttribute.ATTRIBUTE_TYPE.values();
for (BlackboardAttribute.ATTRIBUTE_TYPE attr : stdAttrs) {
if (attr.getLabel().equals(attrTypeStr)) {
return attr;
}
}
return null;
return file;
}
}

View File

@ -19,11 +19,12 @@
package org.sleuthkit.autopsy.externalresults;
import java.util.List;
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
/**
* Interface for parsers that convert some representation of results data (
* e.g., artifacts, derived files, reports) generated by a process external to
* Autopsy into a form ready for import into Autopsy.
* Interface for parsers that convert some representation of results data (e.g.,
* artifacts, derived files, reports) generated by a process external to Autopsy
* into a form ready for import into Autopsy.
*/
public interface ExternalResultsParser {
@ -32,15 +33,17 @@ public interface ExternalResultsParser {
* external to Autopsy and supplied to the parser via its constructor into a
* form ready for import into Autopsy.
*
* @return Results data in a form ready for import into Autopsy.
* @return External results data in a form ready for import into Autopsy.
*/
ExternalResults parse(); // RJCTODO: May need to add data source arg
/**
* Gets error messages describing any errors encountered while parsing the
* input results representation.
* Gets error information describing any errors encountered while parsing
* the input results representation.
*
* @return A possibly empty collection of error message strings.
* @return A collection of error messages, possibly empty. The error
* messages are already logged but are provided to allow the caller to
* provide additional user feedback via the Autopsy user interface.
*/
List<String> getErrorMessages();
List<ErrorInfo> getErrorInfo();
}

View File

@ -1,57 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.externalresults;
/**
* Tags for an external results XML file.
*/
public enum ExternalResultsXML {
ROOT_ELEM("autopsy_results"), //NON-NLS
ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS
ARTIFACT_ELEM("artifact"), //NON-NLS
SOURCE_FILE_ELEM("source_file"), //NON-NLS
PATH_ELEM("path"), //NON-NLS
ATTRIBUTE_ELEM("attribute"), //NON-NLS
VALUE_ELEM("value"), //NON-NLS
SOURCE_MODULE_ELEM("source_module"), //NON-NLS
REPORTS_LIST_ELEM("reports"), //NON-NLS
REPORT_ELEM("report"), //NON-NLS
DISPLAY_NAME_ELEM("display_name"), //NON-NLS
LOCAL_PATH_ELEM("local_path"), //NON-NLS
DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS
DERIVED_FILE_ELEM("derived_file"), //NON-NLS
PARENT_PATH_ELEM("parent_path"), //NON-NLS
TYPE_ATTR("type"), //NON-NLS
NAME_ATTR("name"), //NON-NLS
VALUE_TYPE_TEXT("text"),
VALUE_TYPE_INT32("int32"),
VALUE_TYPE_INT64("int64"),
VALUE_TYPE_DOUBLE("double");
private final String text;
private ExternalResultsXML(final String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}

View File

@ -18,9 +18,12 @@
*/
package org.sleuthkit.autopsy.externalresults;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
import org.sleuthkit.autopsy.coreutils.XMLUtil;
import org.sleuthkit.datamodel.Content;
import org.w3c.dom.Document;
@ -28,8 +31,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Parses an XML representation of externally generated results (artifacts,
* derived files, and reports).
* Parses an XML representation of of results data (e.g., artifacts, derived
* files, reports) generated by a process external to Autopsy into Autopsy.
*/
public final class ExternalResultsXMLParser implements ExternalResultsParser {
@ -37,13 +40,52 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
private static final String XSD_FILE = "autopsy_external_results.xsd"; //NON-NLS
private final String resultsFilePath;
private final ExternalResults externalResults;
private final List<ErrorInfo> errors = new ArrayList<>();
/**
* Tags for an external results XML file.
*/
public enum TagNames {
ROOT_ELEM("autopsy_results"), //NON-NLS
ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS
ARTIFACT_ELEM("artifact"), //NON-NLS
SOURCE_FILE_ELEM("source_file"), //NON-NLS
PATH_ELEM("path"), //NON-NLS
ATTRIBUTE_ELEM("attribute"), //NON-NLS
VALUE_ELEM("value"), //NON-NLS
SOURCE_MODULE_ELEM("source_module"), //NON-NLS
REPORTS_LIST_ELEM("reports"), //NON-NLS
REPORT_ELEM("report"), //NON-NLS
DISPLAY_NAME_ELEM("display_name"), //NON-NLS
LOCAL_PATH_ELEM("local_path"), //NON-NLS
DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS
DERIVED_FILE_ELEM("derived_file"), //NON-NLS
PARENT_PATH_ELEM("parent_path"), //NON-NLS
TYPE_ATTR("type"), //NON-NLS
NAME_ATTR("name"), //NON-NLS
VALUE_TYPE_TEXT("text"),
VALUE_TYPE_INT32("int32"),
VALUE_TYPE_INT64("int64"),
VALUE_TYPE_DOUBLE("double");
private final String text;
private TagNames(final String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
/**
* Constructor.
*
* @param importFilePath Full path of the results file to be parsed.
*/
ExternalResultsXMLParser(Content dataSource, String resultsFilePath) {
public ExternalResultsXMLParser(Content dataSource, String resultsFilePath) {
this.resultsFilePath = resultsFilePath;
externalResults = new ExternalResults(dataSource);
}
@ -59,42 +101,60 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
final Document doc = XMLUtil.loadDoc(ExternalResultsXMLParser.class, this.resultsFilePath, XSD_FILE);
if (doc != null) {
final Element rootElem = doc.getDocumentElement();
if (rootElem != null && rootElem.getNodeName().equals(ExternalResultsXML.ROOT_ELEM.toString())) {
if (rootElem != null && rootElem.getNodeName().equals(TagNames.ROOT_ELEM.toString())) {
parseDerivedFiles(rootElem);
parseArtifacts(rootElem);
parseReports(rootElem);
parseDerivedFiles(rootElem);
} else {
logger.log(Level.SEVERE, "Did not find {0} root element of {2}", new Object[]{
ExternalResultsXML.ROOT_ELEM.toString(), this.resultsFilePath}); //NON-NLS
String errorMessage = String.format("Did not find %s root element of %s", TagNames.ROOT_ELEM.toString(), this.resultsFilePath);
recordError(errorMessage);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing " + this.resultsFilePath, e); //NON-NLS
} catch (Exception ex) {
String errorMessage = String.format("Error parsing %s", this.resultsFilePath);
recordError(errorMessage, ex);
}
return externalResults;
}
@Override
public List<String> getErrorMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
public List<ErrorInfo> getErrorInfo() {
return Collections.unmodifiableList(this.errors);
}
private void parseDerivedFiles(Element rootElement) {
NodeList derivedFilesListNodes = rootElement.getElementsByTagName(TagNames.DERIVED_FILES_LIST_ELEM.toString());
for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) {
Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i);
NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(TagNames.DERIVED_FILE_ELEM.toString());
for (int j = 0; j < derivedFileNodes.getLength(); ++j) {
Element derivedFileElem = (Element) derivedFileNodes.item(j);
String path = getChildElementContent(derivedFileElem, TagNames.LOCAL_PATH_ELEM.toString());
if (path.isEmpty()) {
continue;
}
String parentPath = getChildElementContent((Element) derivedFileNodes.item(j), TagNames.PARENT_PATH_ELEM.toString());
if (parentPath.isEmpty()) {
continue;
}
externalResults.addDerivedFile(path, parentPath);
}
}
}
private void parseArtifacts(final Element root) {
NodeList artifactsListNodes = root.getElementsByTagName(ExternalResultsXML.ARTIFACTS_LIST_ELEM.toString());
NodeList artifactsListNodes = root.getElementsByTagName(TagNames.ARTIFACTS_LIST_ELEM.toString());
for (int i = 0; i < artifactsListNodes.getLength(); ++i) {
Element artifactsListElem = (Element) artifactsListNodes.item(i);
NodeList artifactNodes = artifactsListElem.getElementsByTagName(ExternalResultsXML.ARTIFACT_ELEM.toString());
NodeList artifactNodes = artifactsListElem.getElementsByTagName(TagNames.ARTIFACT_ELEM.toString());
for (int j = 0; j < artifactNodes.getLength(); ++j) {
Element artifactElem = (Element) artifactNodes.item(j);
final String type = getElementAttributeValue(artifactElem, ExternalResultsXML.TYPE_ATTR.toString());
final String type = getElementAttributeValue(artifactElem, TagNames.TYPE_ATTR.toString());
if (!type.isEmpty()) {
Element sourceFileElem = getChildElement(artifactElem, ExternalResultsXML.SOURCE_FILE_ELEM.toString());
if (sourceFileElem != null) {
final String sourceFilePath = this.getChildElementContent((Element) sourceFileElem, ExternalResultsXML.PATH_ELEM.toString());
if (!sourceFilePath.isEmpty()) {
ExternalResults.Artifact artifact = externalResults.addArtifact(type, sourceFilePath);
parseArtifactAttributes(artifactElem, artifact);
}
final String sourceFilePath = this.getChildElementContent((Element) artifactElem, TagNames.PATH_ELEM.toString());
if (!sourceFilePath.isEmpty()) {
ExternalResults.Artifact artifact = externalResults.addArtifact(type, sourceFilePath);
parseArtifactAttributes(artifactElem, artifact);
}
}
}
@ -102,60 +162,60 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
}
private void parseArtifactAttributes(final Element artifactElem, ExternalResults.Artifact artifact) {
NodeList attributeNodesList = artifactElem.getElementsByTagName(ExternalResultsXML.ATTRIBUTE_ELEM.toString());
NodeList attributeNodesList = artifactElem.getElementsByTagName(TagNames.ATTRIBUTE_ELEM.toString());
for (int i = 0; i < attributeNodesList.getLength(); ++i) {
// Get the type of the artifact attribute.
Element attributeElem = (Element) attributeNodesList.item(i);
final String type = getElementAttributeValue(attributeElem, ExternalResultsXML.TYPE_ATTR.toString());
final String type = getElementAttributeValue(attributeElem, TagNames.TYPE_ATTR.toString());
if (type.isEmpty()) {
continue;
}
}
// Get the value of the artifact attribute.
Element valueElem = this.getChildElement(attributeElem, ExternalResultsXML.VALUE_ELEM.toString());
Element valueElem = this.getChildElement(attributeElem, TagNames.VALUE_ELEM.toString());
if (valueElem == null) {
continue;
}
final String value = valueElem.getTextContent();
if (value.isEmpty()) {
logger.log(Level.WARNING, "Found {0} element that has no content in {1}", new Object[]{
ExternalResultsXML.VALUE_ELEM.toString(), this.resultsFilePath});
String errorMessage = String.format("Found %s element that has no content in %s",
TagNames.VALUE_ELEM.toString(), this.resultsFilePath);
recordError(errorMessage);
continue;
}
}
// Get the value type.
String valueType = valueElem.getAttribute(ExternalResultsXML.TYPE_ATTR.toString());
String valueType = valueElem.getAttribute(TagNames.TYPE_ATTR.toString());
if (valueType.isEmpty()) {
valueType = ExternalResultsXML.VALUE_TYPE_TEXT.toString();
}
valueType = TagNames.VALUE_TYPE_TEXT.toString();
}
// Get the source module for the artifact attribute.
String sourceModule = "";
NodeList sourceModuleNodes = attributeElem.getElementsByTagName(ExternalResultsXML.SOURCE_MODULE_ELEM.toString());
NodeList sourceModuleNodes = attributeElem.getElementsByTagName(TagNames.SOURCE_MODULE_ELEM.toString());
if (sourceModuleNodes.getLength() > 0) {
if (sourceModuleNodes.getLength() > 1) {
logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{
ExternalResultsXML.SOURCE_MODULE_ELEM.toString(),
attributeElem.getTagName(),
this.resultsFilePath}); // NON-NLS
String errorMessage = String.format("Found multiple %s child elements for %s element in %s, ignoring all but first occurrence",
TagNames.SOURCE_MODULE_ELEM.toString(), attributeElem.getTagName(), this.resultsFilePath);
recordError(errorMessage);
}
Element srcModuleElem = (Element) sourceModuleNodes.item(0);
sourceModule = srcModuleElem.getTextContent();
}
}
// Add the attribute to the artifact.
artifact.addAttribute(type, value, valueType, sourceModule);
}
}
private void parseReports(Element root) {
NodeList reportsListNodes = root.getElementsByTagName(ExternalResultsXML.REPORTS_LIST_ELEM.toString());
NodeList reportsListNodes = root.getElementsByTagName(TagNames.REPORTS_LIST_ELEM.toString());
for (int i = 0; i < reportsListNodes.getLength(); ++i) {
Element reportsListElem = (Element) reportsListNodes.item(i);
NodeList reportNodes = reportsListElem.getElementsByTagName(ExternalResultsXML.REPORT_ELEM.toString());
NodeList reportNodes = reportsListElem.getElementsByTagName(TagNames.REPORT_ELEM.toString());
for (int j = 0; j < reportNodes.getLength(); ++j) {
Element reportElem = (Element) reportNodes.item(j);
String displayName = getChildElementContent(reportElem, ExternalResultsXML.DISPLAY_NAME_ELEM.toString());
String displayName = getChildElementContent(reportElem, TagNames.DISPLAY_NAME_ELEM.toString());
if (displayName.isEmpty()) {
continue;
}
String path = getChildElementContent(reportElem, ExternalResultsXML.LOCAL_PATH_ELEM.toString());
String path = getChildElementContent(reportElem, TagNames.LOCAL_PATH_ELEM.toString());
if (displayName.isEmpty()) {
continue;
}
@ -164,32 +224,12 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
}
}
private void parseDerivedFiles(Element rootElement) {
NodeList derivedFilesListNodes = rootElement.getElementsByTagName(ExternalResultsXML.DERIVED_FILES_LIST_ELEM.toString());
for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) {
Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i);
NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(ExternalResultsXML.DERIVED_FILE_ELEM.toString());
for (int j = 0; j < derivedFileNodes.getLength(); ++j) {
Element derivedFileElem = (Element) derivedFileNodes.item(j);
String path = getChildElementContent(derivedFileElem, ExternalResultsXML.LOCAL_PATH_ELEM.toString());
if (path.isEmpty()) {
continue;
}
String parentPath = getChildElementContent((Element) derivedFileNodes.item(j), ExternalResultsXML.PARENT_PATH_ELEM.toString());
if (parentPath.isEmpty()) {
continue;
}
externalResults.addDerivedFile(path, parentPath);
}
}
}
private Element getChildElement(Element parentElement, String childElementTagName) {
Element childElem = null;
NodeList childNodes = parentElement.getElementsByTagName(childElementTagName);
if (childNodes.getLength() > 0) {
if (childNodes.getLength() > 1) {
logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{
logger.log(Level.SEVERE, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{
childElementTagName,
parentElement.getTagName(),
this.resultsFilePath}); // NON-NLS
@ -201,7 +241,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
private String getElementAttributeValue(Element element, String attributeName) {
final String attributeValue = element.getAttribute(attributeName);
if (attributeValue.isEmpty()) {
logger.log(Level.WARNING, "Found {0} element missing {1} attribute in {2}", new Object[]{
logger.log(Level.SEVERE, "Found {0} element missing {1} attribute in {2}", new Object[]{
element.getTagName(),
attributeName,
this.resultsFilePath});
@ -214,7 +254,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
NodeList childNodes = parentElement.getElementsByTagName(childElementTagName);
if (childNodes.getLength() > 0) {
if (childNodes.getLength() > 1) {
logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{
logger.log(Level.SEVERE, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{
childElementTagName,
parentElement.getTagName(),
this.resultsFilePath}); // NON-NLS
@ -222,17 +262,27 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser {
Element childElement = (Element) childNodes.item(0);
content = childElement.getTextContent();
if (content.isEmpty()) {
logger.log(Level.WARNING, "Found {0} element with {1} child element that has no content in {2}", new Object[]{
logger.log(Level.SEVERE, "Found {0} element with {1} child element that has no content in {2}", new Object[]{
parentElement.getTagName(),
childElementTagName,
this.resultsFilePath}); // NON-NLS
}
} else {
logger.log(Level.WARNING, "Found {0} element missing {1} child element in {2}", new Object[]{
logger.log(Level.SEVERE, "Found {0} element missing {1} child element in {2}", new Object[]{
parentElement.getTagName(),
childElementTagName,
this.resultsFilePath}); // NON-NLS
}
return content;
}
private void recordError(String errorMessage) {
this.logger.log(Level.SEVERE, errorMessage);
this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage));
}
private void recordError(String errorMessage, Exception ex) {
this.logger.log(Level.SEVERE, errorMessage, ex);
this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage, ex));
}
}