mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into solr-8-upgrade
This commit is contained in:
commit
d10e044473
@ -162,7 +162,7 @@
|
|||||||
<get src="https://drive.google.com/uc?id=1ns2olaWsBu_c4EoE4Seh8t_B3U5RnLKd" dest="${test-input}/CommonFilesAttrs_img1_v1.vhd" skipexisting="true"/>
|
<get src="https://drive.google.com/uc?id=1ns2olaWsBu_c4EoE4Seh8t_B3U5RnLKd" dest="${test-input}/CommonFilesAttrs_img1_v1.vhd" skipexisting="true"/>
|
||||||
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist">
|
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist">
|
||||||
<mkdir dir="${ext.dir}"/>
|
<mkdir dir="${ext.dir}"/>
|
||||||
<copy file="${thirdparty.dir}/LICENSE-2.0.txt" todir="${ext.dir}" />
|
<copy file="${thirdparty.dir}/LICENSE-2.0.txt" todir="${ext.dir}" />
|
||||||
@ -195,5 +195,47 @@
|
|||||||
<globmapper from="*" to="*-MERGED"/>
|
<globmapper from="*" to="*-MERGED"/>
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<!--sets up integration test system properties, calls underlying test-init and then sets up the pathing jar-->
|
||||||
|
<target name="test-init" depends="projectized-common.test-init,getTestDataFiles,qa-functional-pathing-jar" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The paths specified in 'module.run.classpath' are incorporated into the manifest of a jar and then the path to the
|
||||||
|
jar is used as part of the classpath for '-do-junit' instead of 'module.run.classpath'. This was done to prevent
|
||||||
|
classpath length issues on windows. More information on this technique can be found here:
|
||||||
|
https://stackoverflow.com/a/201969.
|
||||||
|
-->
|
||||||
|
<target name="qa-functional-pathing-jar" depends="projectized-common.test-init">
|
||||||
|
<sequential>
|
||||||
|
<!--set up pathing jar based on module.run.classpath as classpath-->
|
||||||
|
<path id="test.qa-functional.pathing-jar.module-cp.classpath" path="${module.run.classpath}"/>
|
||||||
|
<pathconvert pathsep=" " refid="test.qa-functional.pathing-jar.module-cp.classpath" property="test.qa-functional.pathing-jar.module-cp.classpathstr"/>
|
||||||
|
<property name="test.qa-functional.pathing-jar.module-cp.loc" value="${cluster}/test.qa-functional.pathing.module-cp.jar"/>
|
||||||
|
<jar destfile="${test.qa-functional.pathing-jar.module-cp.loc}">
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Class-Path" value="${test.qa-functional.pathing-jar.module-cp.classpathstr}"/>
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
|
||||||
|
<!--grab properties from common.xml:test-init so that "test.qa-functional.run.cp" can be properly formed-->
|
||||||
|
<property name="build.test.qa-functional.dir" location="${build.dir}/test/qa-functional"/>
|
||||||
|
<property name="build.test.qa-functional.classes.dir" location="${build.test.qa-functional.dir}/classes"/>
|
||||||
|
<property name="test.qa-functional.cp.extra" value=""/>
|
||||||
|
|
||||||
|
<!--set up "test.qa-functional.run.cp" to be used by common.xml:-do-junit-->
|
||||||
|
<path id="test.qa-functional.run.cp">
|
||||||
|
<pathelement path="${build.test.qa-functional.classes.dir}"/>
|
||||||
|
<!-- Cannot use <path refid="cp"/> since that uses ${module.classpath} and we want ${module.run.classpath}: -->
|
||||||
|
<pathelement path="${test.qa-functional.runtime.cp}"/>
|
||||||
|
<pathelement path="${cp.extra}"/>
|
||||||
|
<pathelement location="${cluster}/${module.jar}"/>
|
||||||
|
<path refid="test.unit.lib.cp"/>
|
||||||
|
<!-- for compatibility with property based classpath-->
|
||||||
|
<pathelement path="${test.qa-functional.pathing-jar.module-cp.loc}"/>
|
||||||
|
<pathelement path="${test.qa-functional.run.cp.extra}"/>
|
||||||
|
<pathelement path="${test.qa-functional.cp.extra}"/>
|
||||||
|
<pathelement path="${test.extra.nb.javac.deps}"/>
|
||||||
|
</path>
|
||||||
|
</sequential>
|
||||||
|
</target>
|
||||||
</project>
|
</project>
|
||||||
|
@ -96,7 +96,7 @@ Metadata.tableRowTitle.mimeType=MIME Type
|
|||||||
Metadata.tableRowTitle.name=Name
|
Metadata.tableRowTitle.name=Name
|
||||||
Metadata.tableRowTitle.sectorSize=Sector Size
|
Metadata.tableRowTitle.sectorSize=Sector Size
|
||||||
Metadata.tableRowTitle.sha1=SHA1
|
Metadata.tableRowTitle.sha1=SHA1
|
||||||
Metadata.tableRowTitle.sha256=SHA256
|
Metadata.tableRowTitle.sha256=SHA-256
|
||||||
Metadata.tableRowTitle.size=Size
|
Metadata.tableRowTitle.size=Size
|
||||||
Metadata.tableRowTitle.fileNameAlloc=File Name Allocation
|
Metadata.tableRowTitle.fileNameAlloc=File Name Allocation
|
||||||
Metadata.tableRowTitle.metadataAlloc=Metadata Allocation
|
Metadata.tableRowTitle.metadataAlloc=Metadata Allocation
|
||||||
|
@ -137,7 +137,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
|
|||||||
"Metadata.tableRowTitle.mimeType=MIME Type",
|
"Metadata.tableRowTitle.mimeType=MIME Type",
|
||||||
"Metadata.nodeText.truncated=(results truncated)",
|
"Metadata.nodeText.truncated=(results truncated)",
|
||||||
"Metadata.tableRowTitle.sha1=SHA1",
|
"Metadata.tableRowTitle.sha1=SHA1",
|
||||||
"Metadata.tableRowTitle.sha256=SHA256",
|
"Metadata.tableRowTitle.sha256=SHA-256",
|
||||||
"Metadata.tableRowTitle.imageType=Type",
|
"Metadata.tableRowTitle.imageType=Type",
|
||||||
"Metadata.tableRowTitle.sectorSize=Sector Size",
|
"Metadata.tableRowTitle.sectorSize=Sector Size",
|
||||||
"Metadata.tableRowTitle.timezone=Time Zone",
|
"Metadata.tableRowTitle.timezone=Time Zone",
|
||||||
@ -182,6 +182,11 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
|
|||||||
md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
|
md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
|
||||||
}
|
}
|
||||||
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
|
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
|
||||||
|
String sha256 = file.getSha256Hash();
|
||||||
|
if (sha256 == null) {
|
||||||
|
sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
|
||||||
|
}
|
||||||
|
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
|
||||||
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
|
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
|
||||||
addAcquisitionDetails(sb, dataSource);
|
addAcquisitionDetails(sb, dataSource);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers;
|
|||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -97,6 +98,9 @@ final class PDFViewer implements FileTypeViewer {
|
|||||||
|
|
||||||
// Add the IcePDF view to the center of our container.
|
// Add the IcePDF view to the center of our container.
|
||||||
this.container.add(icePdfPanel, BorderLayout.CENTER);
|
this.container.add(icePdfPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// Disable all components until the document is ready to view.
|
||||||
|
enableComponents(container, false);
|
||||||
|
|
||||||
// Document is the 'M' in IcePDFs MVC set up. Read the data needed to
|
// Document is the 'M' in IcePDFs MVC set up. Read the data needed to
|
||||||
// populate the model in the background.
|
// populate the model in the background.
|
||||||
@ -122,12 +126,13 @@ final class PDFViewer implements FileTypeViewer {
|
|||||||
// will cause UI widgets to be updated.
|
// will cause UI widgets to be updated.
|
||||||
try {
|
try {
|
||||||
Document doc = get();
|
Document doc = get();
|
||||||
controller.openDocument(doc, null);
|
controller.openDocument(doc, file.getName());
|
||||||
// This makes the PDF viewer appear as one continuous
|
// This makes the PDF viewer appear as one continuous
|
||||||
// document, which is the default for most popular PDF viewers.
|
// document, which is the default for most popular PDF viewers.
|
||||||
controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
|
controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
|
||||||
// This makes it possible to select text by left clicking and dragging.
|
// This makes it possible to select text by left clicking and dragging.
|
||||||
controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
|
controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
|
||||||
|
enableComponents(container, true);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
@ -140,10 +145,28 @@ final class PDFViewer implements FileTypeViewer {
|
|||||||
file.getId(), file.getName()), ex);
|
file.getId(), file.getName()), ex);
|
||||||
showErrorDialog();
|
showErrorDialog();
|
||||||
}
|
}
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
logger.log(Level.WARNING, String.format("PDF content viewer "
|
||||||
|
+ "was unable to open document with id %d and name %s",
|
||||||
|
file.getId(), file.getName()), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively enable/disable all components in this content viewer.
|
||||||
|
* This will disable/enable all internal IcePDF Swing components too.
|
||||||
|
*/
|
||||||
|
private void enableComponents(Container container, boolean enabled) {
|
||||||
|
Component[] components = container.getComponents();
|
||||||
|
for(Component component : components) {
|
||||||
|
component.setEnabled(enabled);
|
||||||
|
if (component instanceof Container) {
|
||||||
|
enableComponents((Container)component, enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getComponent() {
|
public Component getComponent() {
|
||||||
|
@ -278,6 +278,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
|||||||
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
|
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
|
||||||
"AbstractAbstractFileNode.knownColLbl=Known",
|
"AbstractAbstractFileNode.knownColLbl=Known",
|
||||||
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
|
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
|
||||||
|
"AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash",
|
||||||
"AbstractAbstractFileNode.objectId=Object ID",
|
"AbstractAbstractFileNode.objectId=Object ID",
|
||||||
"AbstractAbstractFileNode.mimeType=MIME Type",
|
"AbstractAbstractFileNode.mimeType=MIME Type",
|
||||||
"AbstractAbstractFileNode.extensionColLbl=Extension"})
|
"AbstractAbstractFileNode.extensionColLbl=Extension"})
|
||||||
@ -305,6 +306,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
|||||||
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
|
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
|
||||||
KNOWN(AbstractAbstractFileNode_knownColLbl()),
|
KNOWN(AbstractAbstractFileNode_knownColLbl()),
|
||||||
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
|
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
|
||||||
|
SHA256HASH(AbstractAbstractFileNode_sha256HashColLbl()),
|
||||||
ObjectID(AbstractAbstractFileNode_objectId()),
|
ObjectID(AbstractAbstractFileNode_objectId()),
|
||||||
MIMETYPE(AbstractAbstractFileNode_mimeType()),
|
MIMETYPE(AbstractAbstractFileNode_mimeType()),
|
||||||
EXTENSION(AbstractAbstractFileNode_extensionColLbl());
|
EXTENSION(AbstractAbstractFileNode_extensionColLbl());
|
||||||
@ -358,6 +360,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
|||||||
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
|
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
|
||||||
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
||||||
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
|
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
|
||||||
|
properties.add(new NodeProperty<>(SHA256HASH.toString(), SHA256HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getSha256Hash())));
|
||||||
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
|
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
|
||||||
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
|
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
|
||||||
|
|
||||||
@ -577,6 +580,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
|||||||
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
|
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
|
||||||
map.put(KNOWN.toString(), content.getKnown().getName());
|
map.put(KNOWN.toString(), content.getKnown().getName());
|
||||||
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
|
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
|
||||||
|
map.put(SHA256HASH.toString(), StringUtils.defaultString(content.getSha256Hash()));
|
||||||
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
|
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
|
||||||
map.put(EXTENSION.toString(), content.getNameExtension());
|
map.put(EXTENSION.toString(), content.getNameExtension());
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time
|
|||||||
AbstractAbstractFileNode.nameColLbl=Name
|
AbstractAbstractFileNode.nameColLbl=Name
|
||||||
AbstractAbstractFileNode.objectId=Object ID
|
AbstractAbstractFileNode.objectId=Object ID
|
||||||
AbstractAbstractFileNode.originalName=Original Name
|
AbstractAbstractFileNode.originalName=Original Name
|
||||||
|
AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash
|
||||||
AbstractAbstractFileNode.sizeColLbl=Size
|
AbstractAbstractFileNode.sizeColLbl=Size
|
||||||
AbstractAbstractFileNode.tagsProperty.displayName=Tags
|
AbstractAbstractFileNode.tagsProperty.displayName=Tags
|
||||||
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
|
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
|
||||||
|
@ -529,9 +529,11 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||||
ResultSet rs = results.getResultSet();) {
|
ResultSet rs = results.getResultSet();) {
|
||||||
|
List<Long> tempList = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
list.add(rs.getLong("artifact_id")); //NON-NLS
|
tempList.add(rs.getLong("artifact_id")); // NON-NLS
|
||||||
}
|
}
|
||||||
|
list.addAll(tempList);
|
||||||
} catch (TskCoreException | SQLException ex) {
|
} catch (TskCoreException | SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
|
LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
|
@ -398,6 +398,20 @@ final class DataSourceInfoUtilities {
|
|||||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||||
return (attr == null) ? null : attr.getValueLong();
|
return (attr == null) ? null : attr.getValueLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the int value of a certain attribute type from an artifact.
|
||||||
|
*
|
||||||
|
* @param artifact The artifact.
|
||||||
|
* @param attributeType The attribute type.
|
||||||
|
*
|
||||||
|
* @return The 'getValueInt()' value or null if the attribute could not be
|
||||||
|
* retrieved.
|
||||||
|
*/
|
||||||
|
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||||
|
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||||
|
return (attr == null) ? null : attr.getValueInt();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the long value of a certain attribute type from an artifact and
|
* Retrieves the long value of a certain attribute type from an artifact and
|
||||||
|
@ -118,7 +118,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
dataSource,
|
dataSource,
|
||||||
DATETIME_ATT,
|
DATETIME_ATT,
|
||||||
DataSourceInfoUtilities.SortOrder.DESCENDING,
|
DataSourceInfoUtilities.SortOrder.DESCENDING,
|
||||||
10);
|
maxCount);
|
||||||
|
|
||||||
List<RecentFileDetails> fileDetails = new ArrayList<>();
|
List<RecentFileDetails> fileDetails = new ArrayList<>();
|
||||||
for (BlackboardArtifact artifact : artifactList) {
|
for (BlackboardArtifact artifact : artifactList) {
|
||||||
@ -134,12 +134,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
|
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
|
||||||
path = attribute.getValueString();
|
path = attribute.getValueString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessedTime != null) {
|
|
||||||
fileDetails.add(new RecentFileDetails(path, accessedTime));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accessedTime != null && accessedTime != 0) {
|
||||||
|
fileDetails.add(new RecentFileDetails(path, accessedTime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileDetails;
|
return fileDetails;
|
||||||
@ -190,7 +189,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
path = attribute.getValueString();
|
path = attribute.getValueString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (accessedTime != null) {
|
if (accessedTime != null && accessedTime != 0L) {
|
||||||
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
|
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +214,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxCount < 0) {
|
||||||
|
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
return createListFromMap(buildAttachmentMap(dataSource), maxCount);
|
return createListFromMap(buildAttachmentMap(dataSource), maxCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +244,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
|
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
|
||||||
if (isMessageArtifact(messageArtifact)) {
|
if (messageArtifact != null && isMessageArtifact(messageArtifact)) {
|
||||||
Content content = artifact.getParent();
|
Content content = artifact.getParent();
|
||||||
if (content instanceof AbstractFile) {
|
if (content instanceof AbstractFile) {
|
||||||
String sender;
|
String sender;
|
||||||
|
@ -1,370 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 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.datasourcesummary.datamodel;
|
|
||||||
|
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides information to populate Top Programs Summary queries.
|
|
||||||
*/
|
|
||||||
public class TopProgramsSummary implements DefaultArtifactUpdateGovernor {
|
|
||||||
|
|
||||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
|
||||||
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
|
||||||
));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A SQL join type.
|
|
||||||
*/
|
|
||||||
private enum JoinType {
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
INNER,
|
|
||||||
OUTER
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A blackboard attribute value column.
|
|
||||||
*/
|
|
||||||
private enum AttributeColumn {
|
|
||||||
value_text,
|
|
||||||
value_int32,
|
|
||||||
value_int64
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The suffix joined to a key name for use as an identifier of a query.
|
|
||||||
*/
|
|
||||||
private static final String QUERY_SUFFIX = "_query";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions that determine the folder name of a list of path elements. If
|
|
||||||
* not matched, function returns null.
|
|
||||||
*/
|
|
||||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
|
||||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
|
||||||
(pathList) -> {
|
|
||||||
if (pathList.size() < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String rootParent = pathList.get(0).toUpperCase();
|
|
||||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
|
||||||
return pathList.get(1);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
|
||||||
(pathList) -> {
|
|
||||||
for (String pathEl : pathList) {
|
|
||||||
String uppered = pathEl.toUpperCase();
|
|
||||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
|
||||||
return "AppData";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a sql statement querying the blackboard attributes table for a
|
|
||||||
* particular attribute type and returning a specified value. That query
|
|
||||||
* also joins with the blackboard artifact table.
|
|
||||||
*
|
|
||||||
* @param joinType The type of join statement to create.
|
|
||||||
* @param attributeColumn The blackboard attribute column that should be
|
|
||||||
* returned.
|
|
||||||
* @param attrType The attribute type to query for.
|
|
||||||
* @param keyName The aliased name of the attribute to return. This
|
|
||||||
* is also used to calculate the alias of the query
|
|
||||||
* same as getFullKey.
|
|
||||||
* @param bbaName The blackboard artifact table alias.
|
|
||||||
*
|
|
||||||
* @return The generated sql statement.
|
|
||||||
*/
|
|
||||||
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
|
|
||||||
String queryName = keyName + QUERY_SUFFIX;
|
|
||||||
String innerQueryName = "inner_attribute_" + queryName;
|
|
||||||
|
|
||||||
return "\n" + joinType + " JOIN (\n"
|
|
||||||
+ " SELECT \n"
|
|
||||||
+ " " + innerQueryName + ".artifact_id,\n"
|
|
||||||
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
|
|
||||||
+ " FROM blackboard_attributes " + innerQueryName + "\n"
|
|
||||||
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
|
|
||||||
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a column key, creates the full name for the column key.
|
|
||||||
*
|
|
||||||
* @param key The column key.
|
|
||||||
*
|
|
||||||
* @return The full identifier for the column key.
|
|
||||||
*/
|
|
||||||
private static String getFullKey(String key) {
|
|
||||||
return key + QUERY_SUFFIX + "." + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a SQL 'where' statement from a list of clauses and puts
|
|
||||||
* parenthesis around each clause.
|
|
||||||
*
|
|
||||||
* @param clauses The clauses
|
|
||||||
*
|
|
||||||
* @return The generated 'where' statement.
|
|
||||||
*/
|
|
||||||
private static String getWhereString(List<String> clauses) {
|
|
||||||
if (clauses.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> parenthesized = clauses.stream()
|
|
||||||
.map(c -> "(" + c + ")")
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a [column] LIKE sql clause.
|
|
||||||
*
|
|
||||||
* @param column The column identifier.
|
|
||||||
* @param likeString The string that will be used as column comparison.
|
|
||||||
* @param isLike if false, the statement becomes NOT LIKE.
|
|
||||||
*
|
|
||||||
* @return The generated statement.
|
|
||||||
*/
|
|
||||||
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
|
||||||
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
private final SleuthkitCaseProvider provider;
|
|
||||||
|
|
||||||
public TopProgramsSummary() {
|
|
||||||
this(SleuthkitCaseProvider.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TopProgramsSummary(SleuthkitCaseProvider provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Integer> getArtifactTypeIdsForRefresh() {
|
|
||||||
return ARTIFACT_UPDATE_TYPE_IDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a list of the top programs used on the data source. Currently
|
|
||||||
* determines this based off of which prefetch results return the highest
|
|
||||||
* count.
|
|
||||||
*
|
|
||||||
* @param dataSource The data source.
|
|
||||||
* @param count The number of programs to return.
|
|
||||||
*
|
|
||||||
* @return The top results objects found.
|
|
||||||
*
|
|
||||||
* @throws SleuthkitCaseProviderException
|
|
||||||
* @throws TskCoreException
|
|
||||||
* @throws SQLException
|
|
||||||
*/
|
|
||||||
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count)
|
|
||||||
throws SleuthkitCaseProviderException, TskCoreException, SQLException {
|
|
||||||
if (dataSource == null || count <= 0) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ntosboot should be ignored
|
|
||||||
final String ntosBootIdentifier = "NTOSBOOT";
|
|
||||||
// programs in windows directory to be ignored
|
|
||||||
final String windowsDir = "/WINDOWS%";
|
|
||||||
|
|
||||||
final String nameParam = "name";
|
|
||||||
final String pathParam = "path";
|
|
||||||
final String runCountParam = "run_count";
|
|
||||||
final String lastRunParam = "last_run";
|
|
||||||
|
|
||||||
String bbaQuery = "bba";
|
|
||||||
|
|
||||||
final String query = "SELECT\n"
|
|
||||||
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
|
|
||||||
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
|
|
||||||
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
|
|
||||||
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
|
|
||||||
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
|
|
||||||
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
|
|
||||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
|
|
||||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
|
|
||||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
|
|
||||||
+ getWhereString(Arrays.asList(
|
|
||||||
bbaQuery + ".artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
|
|
||||||
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
|
|
||||||
// exclude ntosBootIdentifier from results
|
|
||||||
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
|
|
||||||
// exclude windows directory items from results
|
|
||||||
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
|
|
||||||
))
|
|
||||||
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
|
|
||||||
+ "ORDER BY \n"
|
|
||||||
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
|
|
||||||
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
|
|
||||||
+ " " + getFullKey(nameParam) + " ASC";
|
|
||||||
|
|
||||||
DataSourceInfoUtilities.ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
|
|
||||||
List<TopProgramsResult> progResults = new ArrayList<>();
|
|
||||||
|
|
||||||
boolean quitAtCount = false;
|
|
||||||
|
|
||||||
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
|
|
||||||
long lastRunEpoch = resultSet.getLong(lastRunParam);
|
|
||||||
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
|
|
||||||
|
|
||||||
Long runCount = resultSet.getLong(runCountParam);
|
|
||||||
if (resultSet.wasNull()) {
|
|
||||||
runCount = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastRun != null || runCount != null) {
|
|
||||||
quitAtCount = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
progResults.add(new TopProgramsResult(
|
|
||||||
resultSet.getString(nameParam),
|
|
||||||
resultSet.getString(pathParam),
|
|
||||||
runCount,
|
|
||||||
lastRun));
|
|
||||||
}
|
|
||||||
|
|
||||||
return progResults;
|
|
||||||
};
|
|
||||||
|
|
||||||
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query);
|
|
||||||
ResultSet resultSet = dbQuery.getResultSet()) {
|
|
||||||
|
|
||||||
return handler.process(resultSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
|
||||||
*
|
|
||||||
* @param strPath The string path.
|
|
||||||
* @param applicationName The application name.
|
|
||||||
*
|
|
||||||
* @return The short folder name or empty string if not found.
|
|
||||||
*/
|
|
||||||
public String getShortFolderName(String strPath, String applicationName) {
|
|
||||||
if (strPath == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
|
||||||
|
|
||||||
File file = new File(strPath);
|
|
||||||
while (file != null && StringUtils.isNotBlank(file.getName())) {
|
|
||||||
pathEls.add(file.getName());
|
|
||||||
file = file.getParentFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.reverse(pathEls);
|
|
||||||
|
|
||||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
|
||||||
String result = matchEntry.apply(pathEls);
|
|
||||||
if (StringUtils.isNotBlank(result)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes a result of a program run on a datasource.
|
|
||||||
*/
|
|
||||||
public static class TopProgramsResult {
|
|
||||||
|
|
||||||
private final String programName;
|
|
||||||
private final String programPath;
|
|
||||||
private final Long runTimes;
|
|
||||||
private final Date lastRun;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param programName The name of the program.
|
|
||||||
* @param programPath The path of the program.
|
|
||||||
* @param runTimes The number of runs.
|
|
||||||
*/
|
|
||||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
|
||||||
this.programName = programName;
|
|
||||||
this.programPath = programPath;
|
|
||||||
this.runTimes = runTimes;
|
|
||||||
this.lastRun = lastRun;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The name of the program
|
|
||||||
*/
|
|
||||||
public String getProgramName() {
|
|
||||||
return programName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The path of the program.
|
|
||||||
*/
|
|
||||||
public String getProgramPath() {
|
|
||||||
return programPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The number of run times or null if not present.
|
|
||||||
*/
|
|
||||||
public Long getRunTimes() {
|
|
||||||
return runTimes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The last time the program was run or null if not present.
|
|
||||||
*/
|
|
||||||
public Date getLastRun() {
|
|
||||||
return lastRun;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -30,6 +31,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -54,6 +56,36 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
|||||||
*/
|
*/
|
||||||
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions that determine the folder name of a list of path elements. If
|
||||||
|
* not matched, function returns null.
|
||||||
|
*/
|
||||||
|
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||||
|
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||||
|
(pathList) -> {
|
||||||
|
if (pathList.size() < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rootParent = pathList.get(0).toUpperCase();
|
||||||
|
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||||
|
return pathList.get(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||||
|
(pathList) -> {
|
||||||
|
for (String pathEl : pathList) {
|
||||||
|
String uppered = pathEl.toUpperCase();
|
||||||
|
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||||
|
return "AppData";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
|
private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
|
||||||
private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
|
||||||
|
|
||||||
@ -69,17 +101,51 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
|
private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
|
||||||
private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
|
private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
|
||||||
private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
|
private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
|
||||||
|
private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
|
||||||
|
private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
|
||||||
|
private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
|
||||||
|
|
||||||
|
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
|
||||||
|
private static final String WINDOWS_PREFIX = "/WINDOWS";
|
||||||
|
|
||||||
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
|
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
|
||||||
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
|
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts TopProgramsResults pushing highest run time count then most recent
|
||||||
|
* run and then the program name that comes earliest in the alphabet.
|
||||||
|
*/
|
||||||
|
private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
|
||||||
|
// first priority for sorting is the run times
|
||||||
|
// if non-0, this is the return value for the comparator
|
||||||
|
int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
|
||||||
|
if (runTimesCompare != 0) {
|
||||||
|
return -runTimesCompare;
|
||||||
|
}
|
||||||
|
|
||||||
|
// second priority for sorting is the last run date
|
||||||
|
// if non-0, this is the return value for the comparator
|
||||||
|
int lastRunCompare = nullableCompare(
|
||||||
|
a.getLastRun() == null ? null : a.getLastRun().getTime(),
|
||||||
|
b.getLastRun() == null ? null : b.getLastRun().getTime());
|
||||||
|
|
||||||
|
if (lastRunCompare != 0) {
|
||||||
|
return -lastRunCompare;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise sort alphabetically
|
||||||
|
return (a.getProgramName() == null ? "" : a.getProgramName())
|
||||||
|
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
|
||||||
|
};
|
||||||
|
|
||||||
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
|
||||||
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
|
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
|
||||||
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||||
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
|
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
|
||||||
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
|
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
|
||||||
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
|
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
|
||||||
|
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
|
||||||
));
|
));
|
||||||
|
|
||||||
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
|
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
|
||||||
@ -539,6 +605,189 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||||
|
*
|
||||||
|
* @param strPath The string path.
|
||||||
|
* @param applicationName The application name.
|
||||||
|
*
|
||||||
|
* @return The short folder name or empty string if not found.
|
||||||
|
*/
|
||||||
|
public String getShortFolderName(String strPath, String applicationName) {
|
||||||
|
if (strPath == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||||
|
|
||||||
|
File file = new File(strPath);
|
||||||
|
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
|
||||||
|
pathEls.add(file.getName());
|
||||||
|
file = file.getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.reverse(pathEls);
|
||||||
|
|
||||||
|
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||||
|
String result = matchEntry.apply(pathEls);
|
||||||
|
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact.
|
||||||
|
*
|
||||||
|
* @param artifact The TSK_PROG_RUN blackboard artifact.
|
||||||
|
*
|
||||||
|
* @return The generated TopProgramsResult.
|
||||||
|
*/
|
||||||
|
private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
|
||||||
|
String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
|
||||||
|
|
||||||
|
// ignore items with no name or a ntos boot identifier
|
||||||
|
if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
|
||||||
|
|
||||||
|
// ignore windows directory
|
||||||
|
if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
|
||||||
|
Long longCount = (count == null) ? null : (long) count;
|
||||||
|
|
||||||
|
return new TopProgramsResult(
|
||||||
|
programName,
|
||||||
|
path,
|
||||||
|
longCount,
|
||||||
|
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the maximum date given two (possibly null) dates.
|
||||||
|
*
|
||||||
|
* @param date1 First date.
|
||||||
|
* @param date2 Second date.
|
||||||
|
*
|
||||||
|
* @return The maximum non-null date or null if both items are null.
|
||||||
|
*/
|
||||||
|
private static Date getMax(Date date1, Date date2) {
|
||||||
|
if (date1 == null) {
|
||||||
|
return date2;
|
||||||
|
} else if (date2 == null) {
|
||||||
|
return date1;
|
||||||
|
} else {
|
||||||
|
return date1.compareTo(date2) > 0 ? date1 : date2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the compare value favoring the higher non-null number.
|
||||||
|
*
|
||||||
|
* @param long1 First possibly null long.
|
||||||
|
* @param long2 Second possibly null long.
|
||||||
|
*
|
||||||
|
* @return Returns the compare value: 1,0,-1 favoring the higher non-null
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
private static int nullableCompare(Long long1, Long long2) {
|
||||||
|
if (long1 == null && long2 == null) {
|
||||||
|
return 0;
|
||||||
|
} else if (long1 != null && long2 == null) {
|
||||||
|
return 1;
|
||||||
|
} else if (long1 == null && long2 != null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Long.compare(long1, long2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if number is non-null and higher than 0.
|
||||||
|
*
|
||||||
|
* @param longNum The number.
|
||||||
|
*
|
||||||
|
* @return True if non-null and higher than 0.
|
||||||
|
*/
|
||||||
|
private static boolean isPositiveNum(Long longNum) {
|
||||||
|
return longNum != null && longNum > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the top programs results for the given data source limited to
|
||||||
|
* the count provided as a parameter. The highest run times are at the top
|
||||||
|
* of the list. If that information isn't available the last run date is
|
||||||
|
* used. If both, the last run date and the number of run times are
|
||||||
|
* unavailable, the programs will be sorted alphabetically, the count will
|
||||||
|
* be ignored and all items will be returned.
|
||||||
|
*
|
||||||
|
* @param dataSource The datasource. If the datasource is null, an empty
|
||||||
|
* list will be returned.
|
||||||
|
* @param count The number of results to return. This value must be > 0
|
||||||
|
* or an IllegalArgumentException will be thrown.
|
||||||
|
*
|
||||||
|
* @return The sorted list and limited to the count if last run or run count
|
||||||
|
* information is available on any item.
|
||||||
|
*
|
||||||
|
* @throws SleuthkitCaseProviderException
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
assertValidCount(count);
|
||||||
|
|
||||||
|
if (dataSource == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get TopProgramsResults for each TSK_PROG_RUN artifact
|
||||||
|
Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
|
||||||
|
.stream()
|
||||||
|
// convert to a TopProgramsResult object or null if missing critical information
|
||||||
|
.map((art) -> getTopProgramsResult(art))
|
||||||
|
// remove any null items
|
||||||
|
.filter((res) -> res != null)
|
||||||
|
// group by the program name and program path
|
||||||
|
// The value will be a TopProgramsResult with the max run times
|
||||||
|
// and most recent last run date for each program name / program path pair.
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
res -> Pair.of(res.getProgramName(), res.getProgramPath()),
|
||||||
|
res -> res,
|
||||||
|
(res1, res2) -> {
|
||||||
|
return new TopProgramsResult(
|
||||||
|
res1.getProgramName(),
|
||||||
|
res1.getProgramPath(),
|
||||||
|
getMax(res1.getRunTimes(), res2.getRunTimes()),
|
||||||
|
getMax(res1.getLastRun(), res2.getLastRun()));
|
||||||
|
})).values();
|
||||||
|
|
||||||
|
List<TopProgramsResult> orderedResults = results.stream()
|
||||||
|
.sorted(TOP_PROGRAMS_RESULT_COMPARE)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// only limit the list to count if there is no last run date and no run times.
|
||||||
|
if (!orderedResults.isEmpty()) {
|
||||||
|
TopProgramsResult topResult = orderedResults.get(0);
|
||||||
|
// if run times / last run information is available, the first item should have some value,
|
||||||
|
// and then the items should be limited accordingly.
|
||||||
|
if (isPositiveNum(topResult.getRunTimes())
|
||||||
|
|| (topResult.getLastRun() != null && isPositiveNum(topResult.getLastRun().getTime()))) {
|
||||||
|
return orderedResults.stream().limit(count).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise return the alphabetized list with no limit applied.
|
||||||
|
return orderedResults;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing information about a web search artifact.
|
* Object containing information about a web search artifact.
|
||||||
*/
|
*/
|
||||||
@ -722,4 +971,57 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return lastVisit;
|
return lastVisit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a result of a program run on a datasource.
|
||||||
|
*/
|
||||||
|
public static class TopProgramsResult {
|
||||||
|
|
||||||
|
private final String programName;
|
||||||
|
private final String programPath;
|
||||||
|
private final Long runTimes;
|
||||||
|
private final Date lastRun;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param programName The name of the program.
|
||||||
|
* @param programPath The path of the program.
|
||||||
|
* @param runTimes The number of runs.
|
||||||
|
*/
|
||||||
|
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||||
|
this.programName = programName;
|
||||||
|
this.programPath = programPath;
|
||||||
|
this.runTimes = runTimes;
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The name of the program
|
||||||
|
*/
|
||||||
|
public String getProgramName() {
|
||||||
|
return programName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The path of the program.
|
||||||
|
*/
|
||||||
|
public String getProgramPath() {
|
||||||
|
return programPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of run times or null if not present.
|
||||||
|
*/
|
||||||
|
public Long getRunTimes() {
|
||||||
|
return runTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The last time the program was run or null if not present.
|
||||||
|
*/
|
||||||
|
public Date getLastRun() {
|
||||||
|
return lastRun;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,11 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary;
|
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary.TopProgramsResult;
|
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||||
@ -227,35 +226,30 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||||
|
|
||||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||||
private final TopProgramsSummary topProgramsData;
|
private final UserActivitySummary userActivityData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new UserActivityPanel.
|
* Creates a new UserActivityPanel.
|
||||||
*/
|
*/
|
||||||
public UserActivityPanel() {
|
public UserActivityPanel() {
|
||||||
this(new TopProgramsSummary(), new UserActivitySummary());
|
this(new UserActivitySummary());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new UserActivityPanel.
|
* Creates a new UserActivityPanel.
|
||||||
*
|
*
|
||||||
* @param topProgramsData Class from which to obtain top programs data.
|
|
||||||
* @param userActivityData Class from which to obtain remaining user
|
* @param userActivityData Class from which to obtain remaining user
|
||||||
* activity data.
|
* activity data.
|
||||||
*/
|
*/
|
||||||
public UserActivityPanel(
|
public UserActivityPanel(UserActivitySummary userActivityData) {
|
||||||
TopProgramsSummary topProgramsData,
|
super(userActivityData);
|
||||||
UserActivitySummary userActivityData) {
|
this.userActivityData = userActivityData;
|
||||||
|
|
||||||
super(topProgramsData, userActivityData);
|
|
||||||
|
|
||||||
this.topProgramsData = topProgramsData;
|
|
||||||
|
|
||||||
// set up data acquisition methods
|
// set up data acquisition methods
|
||||||
this.dataFetchComponents = Arrays.asList(
|
this.dataFetchComponents = Arrays.asList(
|
||||||
// top programs query
|
// top programs query
|
||||||
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||||
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||||
(result) -> {
|
(result) -> {
|
||||||
showResultWithModuleCheck(topProgramsTable, result,
|
showResultWithModuleCheck(topProgramsTable, result,
|
||||||
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
|
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
|
||||||
@ -307,7 +301,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
* @return The underlying short folder name if one exists.
|
* @return The underlying short folder name if one exists.
|
||||||
*/
|
*/
|
||||||
private String getShortFolderName(String path, String appName) {
|
private String getShortFolderName(String path, String appName) {
|
||||||
return this.topProgramsData.getShortFolderName(path, appName);
|
return this.userActivityData.getShortFolderName(path, appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,6 +49,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
|
|||||||
FileSearchPanel.searchButton.text=Search
|
FileSearchPanel.searchButton.text=Search
|
||||||
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
||||||
HashSearchPanel.md5CheckBox.text=MD5:
|
HashSearchPanel.md5CheckBox.text=MD5:
|
||||||
|
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||||
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
||||||
FileSearchPanel.errorLabel.text=\
|
FileSearchPanel.errorLabel.text=\
|
||||||
DataSourcePanel.dataSourceCheckBox.label=Data Source:
|
DataSourcePanel.dataSourceCheckBox.label=Data Source:
|
||||||
@ -58,3 +59,5 @@ DataSourcePanel.dataSourceNoteLabel.text=*Note: Multiple data sources can be sel
|
|||||||
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
|
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
|
||||||
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
||||||
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
|
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
|
||||||
|
HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||||
|
HashSearchPanel.sha256TextField.text=
|
||||||
|
@ -5,7 +5,9 @@ FileSearchPanel.emptyNode.display.text=No results found.
|
|||||||
HashSearchFilter.errorMessage.emptyHash=Hash data is empty.
|
HashSearchFilter.errorMessage.emptyHash=Hash data is empty.
|
||||||
HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters.
|
HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters.
|
||||||
# {0} - hash data length
|
# {0} - hash data length
|
||||||
HashSearchFilter.errorMessage.wrongLength=Input length({0}), doesn''t match the MD5 length(32).
|
HashSearchFilter.errorMessage.wrongLengthMd5=Input length({0}), doesn''t match the MD5 length(32).
|
||||||
|
# {0} - hash data length
|
||||||
|
HashSearchFilter.errorMessage.wrongLengthSha256=Input length({0}), doesn''t match the SHA-256 length(64).
|
||||||
KnownStatusSearchFilter.errorMessage.noKnownStatusCheckboxSelected=At least one known status checkbox must be selected.
|
KnownStatusSearchFilter.errorMessage.noKnownStatusCheckboxSelected=At least one known status checkbox must be selected.
|
||||||
MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected.
|
MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected.
|
||||||
NameSearchFilter.errorMessage.emtpyName=Please input a name to search.
|
NameSearchFilter.errorMessage.emtpyName=Please input a name to search.
|
||||||
@ -63,6 +65,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
|
|||||||
FileSearchPanel.searchButton.text=Search
|
FileSearchPanel.searchButton.text=Search
|
||||||
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
||||||
HashSearchPanel.md5CheckBox.text=MD5:
|
HashSearchPanel.md5CheckBox.text=MD5:
|
||||||
|
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||||
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
||||||
FileSearchPanel.errorLabel.text=\
|
FileSearchPanel.errorLabel.text=\
|
||||||
DataSourcePanel.dataSourceCheckBox.label=Data Source:
|
DataSourcePanel.dataSourceCheckBox.label=Data Source:
|
||||||
@ -72,3 +75,5 @@ DataSourcePanel.dataSourceNoteLabel.text=*Note: Multiple data sources can be sel
|
|||||||
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
|
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
|
||||||
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
||||||
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
|
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
|
||||||
|
HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||||
|
HashSearchPanel.sha256TextField.text=
|
||||||
|
@ -41,18 +41,36 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return this.getComponent().getHashCheckBox().isSelected();
|
return (this.getComponent().getMd5HashCheckBox().isSelected()
|
||||||
|
|| this.getComponent().getSha256HashCheckBox().isSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPredicate() throws FilterValidationException {
|
public String getPredicate() throws FilterValidationException {
|
||||||
String md5Hash = this.getComponent().getSearchTextField().getText();
|
String predicate = "";
|
||||||
|
if (this.getComponent().getMd5HashCheckBox().isSelected()) {
|
||||||
|
String md5Hash = this.getComponent().getMd5TextField().getText();
|
||||||
|
|
||||||
if (md5Hash.isEmpty()) {
|
if (md5Hash.isEmpty()) {
|
||||||
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
|
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
|
||||||
|
}
|
||||||
|
predicate = "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getComponent().getSha256HashCheckBox().isSelected()) {
|
||||||
|
String sha256Hash = this.getComponent().getSha256TextField().getText();
|
||||||
|
|
||||||
|
if (sha256Hash.isEmpty()) {
|
||||||
|
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
|
||||||
|
}
|
||||||
|
if (predicate.isEmpty()) {
|
||||||
|
predicate = "sha256 = '" + sha256Hash.toLowerCase() + "'"; //NON-NLS
|
||||||
|
} else {
|
||||||
|
predicate = "( " + predicate + " AND sha256 = '" + sha256Hash.toLowerCase() + "')"; //NON-NLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS
|
return predicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,23 +81,45 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
|
|||||||
@Override
|
@Override
|
||||||
@Messages({
|
@Messages({
|
||||||
"HashSearchFilter.errorMessage.emptyHash=Hash data is empty.",
|
"HashSearchFilter.errorMessage.emptyHash=Hash data is empty.",
|
||||||
"# {0} - hash data length", "HashSearchFilter.errorMessage.wrongLength=Input length({0}), doesn''t match the MD5 length(32).",
|
"# {0} - hash data length",
|
||||||
|
"HashSearchFilter.errorMessage.wrongLengthMd5=Input length({0}), doesn''t match the MD5 length(32).",
|
||||||
|
"# {0} - hash data length",
|
||||||
|
"HashSearchFilter.errorMessage.wrongLengthSha256=Input length({0}), doesn''t match the SHA-256 length(64).",
|
||||||
"HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters."
|
"HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters."
|
||||||
})
|
})
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
String inputHashData = this.getComponent().getSearchTextField().getText();
|
if (this.getComponent().getMd5HashCheckBox().isSelected()) {
|
||||||
if (inputHashData.isEmpty()) {
|
String inputHashData = this.getComponent().getMd5TextField().getText();
|
||||||
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
|
if (inputHashData.isEmpty()) {
|
||||||
return false;
|
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inputHashData.length() != 32) {
|
||||||
|
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLengthMd5(inputHashData.length()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!inputHashData.matches("[0-9a-fA-F]+")) {
|
||||||
|
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (inputHashData.length() != 32) {
|
|
||||||
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLength(inputHashData.length()));
|
if (this.getComponent().getSha256HashCheckBox().isSelected()) {
|
||||||
return false;
|
String inputHashData = this.getComponent().getSha256TextField().getText();
|
||||||
}
|
if (inputHashData.isEmpty()) {
|
||||||
if (!inputHashData.matches("[0-9a-fA-F]+")) {
|
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
|
||||||
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
|
return false;
|
||||||
return false;
|
}
|
||||||
|
if (inputHashData.length() != 64) {
|
||||||
|
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLengthSha256(inputHashData.length()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!inputHashData.matches("[0-9a-fA-F]+")) {
|
||||||
|
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,34 +56,67 @@
|
|||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||||
<Component id="hashCheckBox" min="-2" max="-2" attributes="0"/>
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Component id="sha256CheckBox" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="sha256TextField" pref="254" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Component id="md5CheckBox" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="md5TextField" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="searchTextField" min="-2" pref="247" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="103" groupAlignment="3" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<Component id="hashCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
<Component id="searchTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="md5CheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="md5TextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="sha256CheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="sha256TextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JCheckBox" name="hashCheckBox">
|
<Component class="javax.swing.JCheckBox" name="md5CheckBox">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.md5CheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.md5CheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hashCheckBoxActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="md5CheckBoxActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JTextField" name="searchTextField">
|
<Component class="javax.swing.JTextField" name="md5TextField">
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JCheckBox" name="sha256CheckBox">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.sha256CheckBox.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="sha256CheckBoxActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="sha256TextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.sha256TextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -45,19 +45,19 @@ class HashSearchPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
|
|
||||||
searchTextField.setComponentPopupMenu(rightClickMenu);
|
md5TextField.setComponentPopupMenu(rightClickMenu);
|
||||||
ActionListener actList = new ActionListener() {
|
ActionListener actList = new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
JMenuItem jmi = (JMenuItem) e.getSource();
|
JMenuItem jmi = (JMenuItem) e.getSource();
|
||||||
if (jmi.equals(cutMenuItem)) {
|
if (jmi.equals(cutMenuItem)) {
|
||||||
searchTextField.cut();
|
md5TextField.cut();
|
||||||
} else if (jmi.equals(copyMenuItem)) {
|
} else if (jmi.equals(copyMenuItem)) {
|
||||||
searchTextField.copy();
|
md5TextField.copy();
|
||||||
} else if (jmi.equals(pasteMenuItem)) {
|
} else if (jmi.equals(pasteMenuItem)) {
|
||||||
searchTextField.paste();
|
md5TextField.paste();
|
||||||
} else if (jmi.equals(selectAllMenuItem)) {
|
} else if (jmi.equals(selectAllMenuItem)) {
|
||||||
searchTextField.selectAll();
|
md5TextField.selectAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -65,7 +65,24 @@ class HashSearchPanel extends javax.swing.JPanel {
|
|||||||
copyMenuItem.addActionListener(actList);
|
copyMenuItem.addActionListener(actList);
|
||||||
pasteMenuItem.addActionListener(actList);
|
pasteMenuItem.addActionListener(actList);
|
||||||
selectAllMenuItem.addActionListener(actList);
|
selectAllMenuItem.addActionListener(actList);
|
||||||
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() {
|
this.md5TextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sha256TextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(DocumentEvent e) {
|
public void insertUpdate(DocumentEvent e) {
|
||||||
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
@ -84,17 +101,27 @@ class HashSearchPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JCheckBox getHashCheckBox() {
|
JCheckBox getMd5HashCheckBox() {
|
||||||
return hashCheckBox;
|
return md5CheckBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
JTextField getSearchTextField() {
|
JTextField getMd5TextField() {
|
||||||
return searchTextField;
|
return md5TextField;
|
||||||
|
}
|
||||||
|
|
||||||
|
JCheckBox getSha256HashCheckBox() {
|
||||||
|
return sha256CheckBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
JTextField getSha256TextField() {
|
||||||
|
return sha256TextField;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setComponentsEnabled() {
|
void setComponentsEnabled() {
|
||||||
boolean enabled = hashCheckBox.isSelected();
|
boolean md5Enabled = md5CheckBox.isSelected();
|
||||||
this.searchTextField.setEnabled(enabled);
|
this.md5TextField.setEnabled(md5Enabled);
|
||||||
|
boolean sha256Enabled = sha256CheckBox.isSelected();
|
||||||
|
this.sha256TextField.setEnabled(sha256Enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,8 +138,10 @@ class HashSearchPanel extends javax.swing.JPanel {
|
|||||||
copyMenuItem = new javax.swing.JMenuItem();
|
copyMenuItem = new javax.swing.JMenuItem();
|
||||||
pasteMenuItem = new javax.swing.JMenuItem();
|
pasteMenuItem = new javax.swing.JMenuItem();
|
||||||
selectAllMenuItem = new javax.swing.JMenuItem();
|
selectAllMenuItem = new javax.swing.JMenuItem();
|
||||||
hashCheckBox = new javax.swing.JCheckBox();
|
md5CheckBox = new javax.swing.JCheckBox();
|
||||||
searchTextField = new javax.swing.JTextField();
|
md5TextField = new javax.swing.JTextField();
|
||||||
|
sha256CheckBox = new javax.swing.JCheckBox();
|
||||||
|
sha256TextField = new javax.swing.JTextField();
|
||||||
|
|
||||||
cutMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.cutMenuItem.text")); // NOI18N
|
cutMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.cutMenuItem.text")); // NOI18N
|
||||||
rightClickMenu.add(cutMenuItem);
|
rightClickMenu.add(cutMenuItem);
|
||||||
@ -126,48 +155,75 @@ class HashSearchPanel extends javax.swing.JPanel {
|
|||||||
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.selectAllMenuItem.text")); // NOI18N
|
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.selectAllMenuItem.text")); // NOI18N
|
||||||
rightClickMenu.add(selectAllMenuItem);
|
rightClickMenu.add(selectAllMenuItem);
|
||||||
|
|
||||||
hashCheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
|
md5CheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
|
||||||
hashCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
md5CheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
hashCheckBoxActionPerformed(evt);
|
md5CheckBoxActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sha256CheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.sha256CheckBox.text")); // NOI18N
|
||||||
|
sha256CheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
sha256CheckBoxActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sha256TextField.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.sha256TextField.text")); // NOI18N
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addGap(0, 0, 0)
|
.addGap(0, 0, 0)
|
||||||
.addComponent(hashCheckBox)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(sha256CheckBox)
|
||||||
.addGap(0, 0, 0))
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(sha256TextField, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE))
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(md5CheckBox)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(md5TextField)))
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(hashCheckBox)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
.addComponent(md5CheckBox)
|
||||||
|
.addComponent(md5TextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(sha256CheckBox)
|
||||||
|
.addComponent(sha256TextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void hashCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashCheckBoxActionPerformed
|
private void md5CheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_md5CheckBoxActionPerformed
|
||||||
setComponentsEnabled();
|
setComponentsEnabled();
|
||||||
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
}//GEN-LAST:event_hashCheckBoxActionPerformed
|
}//GEN-LAST:event_md5CheckBoxActionPerformed
|
||||||
|
|
||||||
|
private void sha256CheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sha256CheckBoxActionPerformed
|
||||||
|
setComponentsEnabled();
|
||||||
|
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||||
|
}//GEN-LAST:event_sha256CheckBoxActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JMenuItem copyMenuItem;
|
private javax.swing.JMenuItem copyMenuItem;
|
||||||
private javax.swing.JMenuItem cutMenuItem;
|
private javax.swing.JMenuItem cutMenuItem;
|
||||||
private javax.swing.JCheckBox hashCheckBox;
|
private javax.swing.JCheckBox md5CheckBox;
|
||||||
|
private javax.swing.JTextField md5TextField;
|
||||||
private javax.swing.JMenuItem pasteMenuItem;
|
private javax.swing.JMenuItem pasteMenuItem;
|
||||||
private javax.swing.JPopupMenu rightClickMenu;
|
private javax.swing.JPopupMenu rightClickMenu;
|
||||||
private javax.swing.JTextField searchTextField;
|
|
||||||
private javax.swing.JMenuItem selectAllMenuItem;
|
private javax.swing.JMenuItem selectAllMenuItem;
|
||||||
|
private javax.swing.JCheckBox sha256CheckBox;
|
||||||
|
private javax.swing.JTextField sha256TextField;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
void addActionListener(ActionListener l) {
|
void addActionListener(ActionListener l) {
|
||||||
searchTextField.addActionListener(l);
|
md5TextField.addActionListener(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1274,6 +1274,9 @@ final class IngestJobPipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a data source had no tasks in progress it may now be complete.
|
||||||
|
checkForStageCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.modules.hashdatabase;
|
package org.sleuthkit.autopsy.modules.hashdatabase;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -208,10 +208,17 @@ public class HashDbIngestModule implements FileIngestModule {
|
|||||||
// Safely get a reference to the totalsForIngestJobs object
|
// Safely get a reference to the totalsForIngestJobs object
|
||||||
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
|
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
|
||||||
|
|
||||||
// calc hash value
|
// calc hash values
|
||||||
String md5Hash = getHash(file, totals);
|
try {
|
||||||
if (md5Hash == null) {
|
calculateHashes(file, totals);
|
||||||
return ProcessResult.ERROR;
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
|
||||||
|
services.postMessage(IngestMessage.createErrorMessage(
|
||||||
|
HashLookupModuleFactory.getModuleName(),
|
||||||
|
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", file.getName()),
|
||||||
|
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
|
||||||
|
file.getParentPath() + file.getName(),
|
||||||
|
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? "Allocated File" : "Deleted File")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// the processing result of handling this file
|
// the processing result of handling this file
|
||||||
@ -451,50 +458,46 @@ public class HashDbIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the md5 hash for a file or generates one if no one exists on
|
* Generates hashes for the given file if they haven't already been set.
|
||||||
* the file.
|
* Hashes are saved to the AbstractFile object.
|
||||||
*
|
*
|
||||||
* @param file The file in order to determine the hash.
|
* @param file The file in order to determine the hash.
|
||||||
* @param totals The timing metrics for this process.
|
* @param totals The timing metrics for this process.
|
||||||
*
|
|
||||||
* @return The found or determined md5 hash or null if none could be
|
|
||||||
* determined.
|
|
||||||
*/
|
*/
|
||||||
private String getHash(AbstractFile file, IngestJobTotals totals) {
|
private void calculateHashes(AbstractFile file, IngestJobTotals totals) throws TskCoreException {
|
||||||
|
|
||||||
|
// First check if we've already calculated the hashes.
|
||||||
String md5Hash = file.getMd5Hash();
|
String md5Hash = file.getMd5Hash();
|
||||||
if (md5Hash != null && md5Hash.isEmpty()) {
|
String sha256Hash = file.getSha256Hash();
|
||||||
return md5Hash;
|
if ((md5Hash != null && ! md5Hash.isEmpty())
|
||||||
|
&& (sha256Hash != null && ! sha256Hash.isEmpty())) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
||||||
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
long calcstart = System.currentTimeMillis();
|
||||||
long calcstart = System.currentTimeMillis();
|
List<HashUtility.HashResult> newHashResults =
|
||||||
md5Hash = HashUtility.calculateMd5Hash(file);
|
HashUtility.calculateHashes(file, Arrays.asList(HashUtility.HashType.MD5,HashUtility.HashType.SHA256 ));
|
||||||
if (file.getSize() > 0) {
|
if (file.getSize() > 0) {
|
||||||
// Surprisingly, the hash calculation does not seem to be correlated that
|
// Surprisingly, the hash calculation does not seem to be correlated that
|
||||||
// strongly with file size until the files get large.
|
// strongly with file size until the files get large.
|
||||||
// Only normalize if the file size is greater than ~1MB.
|
// Only normalize if the file size is greater than ~1MB.
|
||||||
if (file.getSize() < 1000000) {
|
if (file.getSize() < 1000000) {
|
||||||
HealthMonitor.submitTimingMetric(metric);
|
HealthMonitor.submitTimingMetric(metric);
|
||||||
} else {
|
} else {
|
||||||
// In testing, this normalization gave reasonable resuls
|
// In testing, this normalization gave reasonable resuls
|
||||||
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
|
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
file.setMd5Hash(md5Hash);
|
|
||||||
long delta = (System.currentTimeMillis() - calcstart);
|
|
||||||
totals.totalCalctime.addAndGet(delta);
|
|
||||||
return md5Hash;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
|
|
||||||
services.postMessage(IngestMessage.createErrorMessage(
|
|
||||||
HashLookupModuleFactory.getModuleName(),
|
|
||||||
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", file.getName()),
|
|
||||||
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
|
|
||||||
file.getParentPath() + file.getName(),
|
|
||||||
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? "Allocated File" : "Deleted File")));
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
for (HashUtility.HashResult hash : newHashResults) {
|
||||||
|
if (hash.getType().equals(HashUtility.HashType.MD5)) {
|
||||||
|
file.setMd5Hash(hash.getValue());
|
||||||
|
} else if (hash.getType().equals(HashUtility.HashType.SHA256)) {
|
||||||
|
file.setSha256Hash(hash.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long delta = (System.currentTimeMillis() - calcstart);
|
||||||
|
totals.totalCalctime.addAndGet(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -198,7 +198,6 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
|
|||||||
// for extracted virtual machines there is no manifest XML file to read data source ID from so use parent data source ID.
|
// for extracted virtual machines there is no manifest XML file to read data source ID from so use parent data source ID.
|
||||||
// ingest the data sources
|
// ingest the data sources
|
||||||
ingestVirtualMachineImage(Paths.get(folder, file));
|
ingestVirtualMachineImage(Paths.get(folder, file));
|
||||||
logger.log(Level.INFO, "Ingest complete for virtual machine file {0} in folder {1}", new Object[]{file, folder}); //NON-NLS
|
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
logger.log(Level.INFO, "Interrupted while ingesting virtual machine file " + file + " in folder " + folder, ex); //NON-NLS
|
logger.log(Level.INFO, "Interrupted while ingesting virtual machine file " + file + " in folder " + folder, ex); //NON-NLS
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@ -287,8 +286,8 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the image was added, analyze it with the ingest modules for this
|
* If the image was added, start analysis on it with the ingest modules for this
|
||||||
* ingest context.
|
* ingest context. Note that this does not wait for ingest to complete.
|
||||||
*/
|
*/
|
||||||
if (!dspCallback.vmDataSources.isEmpty()) {
|
if (!dspCallback.vmDataSources.isEmpty()) {
|
||||||
Case.getCurrentCaseThrows().notifyDataSourceAdded(dspCallback.vmDataSources.get(0), taskId);
|
Case.getCurrentCaseThrows().notifyDataSourceAdded(dspCallback.vmDataSources.get(0), taskId);
|
||||||
@ -300,7 +299,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
|
|||||||
IngestServices.getInstance().postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO,
|
IngestServices.getInstance().postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO,
|
||||||
VMExtractorIngestModuleFactory.getModuleName(),
|
VMExtractorIngestModuleFactory.getModuleName(),
|
||||||
NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.addedVirtualMachineImage.message", vmFile.toString())));
|
NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.addedVirtualMachineImage.message", vmFile.toString())));
|
||||||
IngestManager.getInstance().queueIngestJob(dataSourceContent, ingestJobSettings);
|
IngestManager.getInstance().beginIngestJob(dataSourceContent, ingestJobSettings);
|
||||||
} else {
|
} else {
|
||||||
Case.getCurrentCaseThrows().notifyFailedAddingDataSource(taskId);
|
Case.getCurrentCaseThrows().notifyFailedAddingDataSource(taskId);
|
||||||
}
|
}
|
||||||
|
@ -1117,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
|
|
||||||
newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
|
newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
|
||||||
abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
|
abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
|
||||||
abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
|
abstractFile.getMd5Hash(), abstractFile.getSha256Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
|
||||||
true, TskData.EncodingType.NONE,
|
true, TskData.EncodingType.NONE,
|
||||||
newParent, trans);
|
newParent, trans);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
@ -1 +1,3 @@
|
|||||||
Netbeans platform does not properly scope classloaders while running qa-functional test code. The result is that NoClassDefError's occur in instances where an external jar (i.e. importing a class from common-io) is referenced in test code and the same external jar is referenced in multiple NBM's. Importing from external jars in qa-functional should be avoided. See jira issue 6954 for more information.
|
Netbeans platform does not properly scope classloaders while running qa-functional test code. The result is that NoClassDefError's occur in instances where an external jar (i.e. importing a class from common-io) is referenced in test code and the same external jar is referenced in multiple NBM's. Importing from external jars in qa-functional should be avoided. See jira issue 6954 for more information.
|
||||||
|
|
||||||
|
Many of the functional tests require external data sources. The ant target 'getTestDataFiles' must be run successfully to download the files. This should occur as a part of the 'test-init' ant target in build.xml.
|
@ -1256,7 +1256,8 @@ public class CentralRepoDatamodelTest extends TestCase {
|
|||||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
||||||
|
|
||||||
// We expect 11 total - 10 default and the custom one made earlier
|
// We expect 11 total - 10 default and the custom one made earlier
|
||||||
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " entries - expected 11", types.size() == 11);
|
// Note: this test will need to be updated based on the current default items defined in the correlation_types table
|
||||||
|
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " entries - expected 28", types.size() == 28);
|
||||||
} catch (CentralRepoException ex) {
|
} catch (CentralRepoException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
Assert.fail(ex.getMessage());
|
Assert.fail(ex.getMessage());
|
||||||
@ -1267,7 +1268,8 @@ public class CentralRepoDatamodelTest extends TestCase {
|
|||||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getEnabledCorrelationTypes();
|
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getEnabledCorrelationTypes();
|
||||||
|
|
||||||
// We expect 10 - the custom type is disabled
|
// We expect 10 - the custom type is disabled
|
||||||
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " enabled entries - expected 10", types.size() == 10);
|
// Note: this test will need to be updated based on the current default items defined in the correlation_types table
|
||||||
|
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " enabled entries - expected 27", types.size() == 27);
|
||||||
} catch (CentralRepoException ex) {
|
} catch (CentralRepoException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
Assert.fail(ex.getMessage());
|
Assert.fail(ex.getMessage());
|
||||||
@ -1278,7 +1280,8 @@ public class CentralRepoDatamodelTest extends TestCase {
|
|||||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getSupportedCorrelationTypes();
|
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getSupportedCorrelationTypes();
|
||||||
|
|
||||||
// We expect 10 - the custom type is not supported
|
// We expect 10 - the custom type is not supported
|
||||||
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " supported entries - expected 10", types.size() == 10);
|
// Note: this test will need to be updated based on the current default items defined in the correlation_types table
|
||||||
|
assertTrue("getDefinedCorrelationTypes returned " + types.size() + " supported entries - expected 27", types.size() == 27);
|
||||||
} catch (CentralRepoException ex) {
|
} catch (CentralRepoException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
Assert.fail(ex.getMessage());
|
Assert.fail(ex.getMessage());
|
||||||
|
@ -218,7 +218,9 @@ class InterCaseTestUtils {
|
|||||||
// kitchenSink.add(keywordSearchTemplate);
|
// kitchenSink.add(keywordSearchTemplate);
|
||||||
|
|
||||||
this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink);
|
this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupCorrelationTypes() {
|
||||||
try {
|
try {
|
||||||
Collection<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
Collection<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
||||||
|
|
||||||
@ -254,16 +256,16 @@ class InterCaseTestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Long, String> getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException {
|
Map<Long, String> getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException {
|
||||||
return DataSourceLoader.getAllDataSources();
|
return DataSourceLoader.getAllDataSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Integer> getCaseMap() throws CentralRepoException {
|
Map<String, Integer> getCaseMap() throws CentralRepoException {
|
||||||
|
|
||||||
if (CentralRepository.isEnabled()) {
|
if (CentralRepository.isEnabled()) {
|
||||||
Map<String, Integer> mapOfCaseIdsToCase = new HashMap<>();
|
Map<String, Integer> mapOfCaseIdsToCase = new HashMap<>();
|
||||||
|
|
||||||
for (CorrelationCase correlationCase : CentralRepository.getInstance().getCases()) {
|
for (CorrelationCase correlationCase : CentralRepository.getInstance().getCases()) {
|
||||||
mapOfCaseIdsToCase.put(correlationCase.getDisplayName(), correlationCase.getID());
|
mapOfCaseIdsToCase.put(correlationCase.getDisplayName(), correlationCase.getID());
|
||||||
}
|
}
|
||||||
@ -300,7 +302,7 @@ class InterCaseTestUtils {
|
|||||||
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, crSettings);
|
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, crSettings);
|
||||||
centralRepoSchemaFactory.initializeDatabaseSchema();
|
centralRepoSchemaFactory.initializeDatabaseSchema();
|
||||||
centralRepoSchemaFactory.insertDefaultDatabaseContent();
|
centralRepoSchemaFactory.insertDefaultDatabaseContent();
|
||||||
|
|
||||||
crSettings.saveSettings();
|
crSettings.saveSettings();
|
||||||
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.SQLITE);
|
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.SQLITE);
|
||||||
}
|
}
|
||||||
@ -313,10 +315,10 @@ class InterCaseTestUtils {
|
|||||||
* The length of caseNames and caseDataSourcePaths should be the same, and
|
* The length of caseNames and caseDataSourcePaths should be the same, and
|
||||||
* cases should appear in the same order.
|
* cases should appear in the same order.
|
||||||
*
|
*
|
||||||
* @param caseNames list case names
|
* @param caseNames list case names
|
||||||
* @param caseDataSourcePaths two dimensional array listing the datasources
|
* @param caseDataSourcePaths two dimensional array listing the datasources
|
||||||
* in each case
|
* in each case
|
||||||
* @param ingestJobSettings HashLookup FileType etc...
|
* @param ingestJobSettings HashLookup FileType etc...
|
||||||
* @param caseReferenceToStore
|
* @param caseReferenceToStore
|
||||||
*/
|
*/
|
||||||
Case createCases(String[] caseNames, Path[][] caseDataSourcePaths, IngestJobSettings ingestJobSettings, String caseReferenceToStore) throws TskCoreException {
|
Case createCases(String[] caseNames, Path[][] caseDataSourcePaths, IngestJobSettings ingestJobSettings, String caseReferenceToStore) throws TskCoreException {
|
||||||
|
@ -253,7 +253,7 @@ public class IngestFileFiltersTest extends NbTestCase {
|
|||||||
IngestUtils.runIngestJob(currentCase.getDataSources(), ingestJobSettings);
|
IngestUtils.runIngestJob(currentCase.getDataSources(), ingestJobSettings);
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> results = fileManager.findFiles("%%");
|
List<AbstractFile> results = fileManager.findFiles("%%");
|
||||||
assertEquals(70, results.size());
|
assertEquals(71, results.size());
|
||||||
int carvedJpgGifFiles = 0;
|
int carvedJpgGifFiles = 0;
|
||||||
for (AbstractFile file : results) {
|
for (AbstractFile file : results) {
|
||||||
if (file.getNameExtension().equalsIgnoreCase("jpg") || file.getNameExtension().equalsIgnoreCase("gif")) { //Unalloc file and .jpg files in dir1, dir2, $CarvedFiles, root directory should have MIME type
|
if (file.getNameExtension().equalsIgnoreCase("jpg") || file.getNameExtension().equalsIgnoreCase("gif")) { //Unalloc file and .jpg files in dir1, dir2, $CarvedFiles, root directory should have MIME type
|
||||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@ -35,6 +36,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.autopsy.testutils.TskMockUtils;
|
import org.sleuthkit.autopsy.testutils.TskMockUtils;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
import org.sleuthkit.autopsy.testutils.RandomizationUtils;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE;
|
import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE;
|
||||||
@ -42,7 +44,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALU
|
|||||||
/**
|
/**
|
||||||
* Unit tests for DataSourceInfoUtilities.getArtifacts
|
* Unit tests for DataSourceInfoUtilities.getArtifacts
|
||||||
*/
|
*/
|
||||||
public class GetArtifactsTest {
|
public class DataSourceInfoUtilitiesTest {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ExpectedException thrown = ExpectedException.none();
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
@ -145,38 +147,13 @@ public class GetArtifactsTest {
|
|||||||
|
|
||||||
List<BlackboardArtifact> toRet = new ArrayList<>();
|
List<BlackboardArtifact> toRet = new ArrayList<>();
|
||||||
for (int i = 0; i < values.size(); i++) {
|
for (int i = 0; i < values.size(); i++) {
|
||||||
toRet.add(TskMockUtils.mockArtifact(new BlackboardArtifact.Type(artifactType), 1000 + i, dataSource,
|
toRet.add(TskMockUtils.getArtifact(new BlackboardArtifact.Type(artifactType), 1000 + i, dataSource,
|
||||||
attrMaker.make(attrType, "TEST SOURCE", values.get(i))));
|
attrMaker.make(attrType, "TEST SOURCE", values.get(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return toRet;
|
return toRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns list in 0, n-1, 1, n-2 ... order. Deterministic so same results
|
|
||||||
* each time, but not in original order.
|
|
||||||
*
|
|
||||||
* @return Mixed up list.
|
|
||||||
*/
|
|
||||||
private <T> List<T> getMixedUp(List<T> list) {
|
|
||||||
int forward = 0;
|
|
||||||
int backward = list.size() - 1;
|
|
||||||
|
|
||||||
List<T> newList = new ArrayList<>();
|
|
||||||
while (forward <= backward) {
|
|
||||||
newList.add(list.get(forward));
|
|
||||||
|
|
||||||
if (forward < backward) {
|
|
||||||
newList.add(list.get(backward));
|
|
||||||
}
|
|
||||||
|
|
||||||
forward++;
|
|
||||||
backward--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a basic test passing a list of generated artifacts in mixed up order
|
* Does a basic test passing a list of generated artifacts in mixed up order
|
||||||
* to DataSourceInfoUtilities.getArtifacts and expecting a sorted list to be
|
* to DataSourceInfoUtilities.getArtifacts and expecting a sorted list to be
|
||||||
@ -194,11 +171,11 @@ public class GetArtifactsTest {
|
|||||||
private <T> void testSorted(ARTIFACT_TYPE artifactType, ATTRIBUTE_TYPE attrType, List<T> values,
|
private <T> void testSorted(ARTIFACT_TYPE artifactType, ATTRIBUTE_TYPE attrType, List<T> values,
|
||||||
AttrMaker<T> attrMaker, SortOrder sortOrder, int count) throws TskCoreException {
|
AttrMaker<T> attrMaker, SortOrder sortOrder, int count) throws TskCoreException {
|
||||||
|
|
||||||
DataSource dataSource = TskMockUtils.mockDataSource(1);
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
List<BlackboardArtifact> sortedArtifacts = getArtifacts(artifactType, new BlackboardAttribute.Type(attrType),
|
List<BlackboardArtifact> sortedArtifacts = getArtifacts(artifactType, new BlackboardAttribute.Type(attrType),
|
||||||
dataSource, values, attrMaker);
|
dataSource, values, attrMaker);
|
||||||
|
|
||||||
List<BlackboardArtifact> mixedUpArtifacts = getMixedUp(sortedArtifacts);
|
List<BlackboardArtifact> mixedUpArtifacts = RandomizationUtils.getMixedUp(sortedArtifacts);
|
||||||
|
|
||||||
List<BlackboardArtifact> expectedArtifacts = count == 0
|
List<BlackboardArtifact> expectedArtifacts = count == 0
|
||||||
? sortedArtifacts
|
? sortedArtifacts
|
||||||
@ -250,17 +227,17 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortAscending() throws TskCoreException {
|
public void getArtifacts_sortAscending() throws TskCoreException {
|
||||||
testAscDesc(SortOrder.ASCENDING);
|
testAscDesc(SortOrder.ASCENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSortDescending() throws TskCoreException {
|
public void getArtifacts_sortDescending() throws TskCoreException {
|
||||||
testAscDesc(SortOrder.DESCENDING);
|
testAscDesc(SortOrder.DESCENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimits() throws TskCoreException {
|
public void getArtifacts_limits() throws TskCoreException {
|
||||||
List<Integer> integers = Arrays.asList(22, 31, 42, 50, 60);
|
List<Integer> integers = Arrays.asList(22, 31, 42, 50, 60);
|
||||||
testSorted(ARTIFACT_TYPE.TSK_PROG_RUN, ATTRIBUTE_TYPE.TSK_COUNT, integers, BlackboardAttribute::new, SortOrder.ASCENDING, 3);
|
testSorted(ARTIFACT_TYPE.TSK_PROG_RUN, ATTRIBUTE_TYPE.TSK_COUNT, integers, BlackboardAttribute::new, SortOrder.ASCENDING, 3);
|
||||||
testSorted(ARTIFACT_TYPE.TSK_PROG_RUN, ATTRIBUTE_TYPE.TSK_COUNT, integers, BlackboardAttribute::new, SortOrder.ASCENDING, 5);
|
testSorted(ARTIFACT_TYPE.TSK_PROG_RUN, ATTRIBUTE_TYPE.TSK_COUNT, integers, BlackboardAttribute::new, SortOrder.ASCENDING, 5);
|
||||||
@ -281,11 +258,11 @@ public class GetArtifactsTest {
|
|||||||
private <T> void testFailOnBadAttrType(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, T val,
|
private <T> void testFailOnBadAttrType(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, T val,
|
||||||
AttrMaker<T> attrMaker) throws TskCoreException {
|
AttrMaker<T> attrMaker) throws TskCoreException {
|
||||||
|
|
||||||
DataSource dataSource = TskMockUtils.mockDataSource(1);
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
List<BlackboardArtifact> artifacts = Arrays.asList(
|
List<BlackboardArtifact> artifacts = Arrays.asList(
|
||||||
TskMockUtils.mockArtifact(artifactType, 2, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val)),
|
TskMockUtils.getArtifact(artifactType, 2, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val)),
|
||||||
TskMockUtils.mockArtifact(artifactType, 3, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val))
|
TskMockUtils.getArtifact(artifactType, 3, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val))
|
||||||
);
|
);
|
||||||
test(artifactType,
|
test(artifactType,
|
||||||
dataSource,
|
dataSource,
|
||||||
@ -299,7 +276,7 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailOnJson() throws TskCoreException {
|
public void getArtifacts_failOnJson() throws TskCoreException {
|
||||||
testFailOnBadAttrType(
|
testFailOnBadAttrType(
|
||||||
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_GPS_ROUTE),
|
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_GPS_ROUTE),
|
||||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS),
|
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS),
|
||||||
@ -308,7 +285,7 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailOnBytes() throws TskCoreException {
|
public void getArtifacts_failOnBytes() throws TskCoreException {
|
||||||
testFailOnBadAttrType(
|
testFailOnBadAttrType(
|
||||||
new BlackboardArtifact.Type(999, "BYTE_ARRAY_TYPE", "Byte Array Type"),
|
new BlackboardArtifact.Type(999, "BYTE_ARRAY_TYPE", "Byte Array Type"),
|
||||||
new BlackboardAttribute.Type(999, "BYTE_ARR_ATTR_TYPE", "Byte Array Attribute Type", TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE),
|
new BlackboardAttribute.Type(999, "BYTE_ARR_ATTR_TYPE", "Byte Array Attribute Type", TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE),
|
||||||
@ -317,20 +294,20 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPurgeAttrNotPresent() throws TskCoreException {
|
public void getArtifacts_purgeAttrNotPresent() throws TskCoreException {
|
||||||
long day = 24 * 60 * 60;
|
long day = 24 * 60 * 60;
|
||||||
DataSource dataSource = TskMockUtils.mockDataSource(1);
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
BlackboardArtifact.Type ART_TYPE = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_PROG_RUN);
|
BlackboardArtifact.Type ART_TYPE = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_PROG_RUN);
|
||||||
|
|
||||||
BlackboardArtifact mock1 = TskMockUtils.mockArtifact(ART_TYPE, 10, dataSource,
|
BlackboardArtifact mock1 = TskMockUtils.getArtifact(ART_TYPE, 10, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 5),
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 5),
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", day));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", day));
|
||||||
|
|
||||||
BlackboardArtifact mock2 = TskMockUtils.mockArtifact(ART_TYPE, 20, dataSource,
|
BlackboardArtifact mock2 = TskMockUtils.getArtifact(ART_TYPE, 20, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 6));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 6));
|
||||||
|
|
||||||
BlackboardArtifact mock3 = TskMockUtils.mockArtifact(ART_TYPE, 30, dataSource,
|
BlackboardArtifact mock3 = TskMockUtils.getArtifact(ART_TYPE, 30, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 7),
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 7),
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", 3 * day));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", 3 * day));
|
||||||
|
|
||||||
@ -346,20 +323,20 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultAttrsPresent() throws TskCoreException {
|
public void getArtifacts_multipleAttrsPresent() throws TskCoreException {
|
||||||
long day = 24 * 60 * 60;
|
long day = 24 * 60 * 60;
|
||||||
DataSource dataSource = TskMockUtils.mockDataSource(1);
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
BlackboardArtifact.Type ART_TYPE = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_PROG_RUN);
|
BlackboardArtifact.Type ART_TYPE = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_PROG_RUN);
|
||||||
|
|
||||||
BlackboardArtifact mock1 = TskMockUtils.mockArtifact(ART_TYPE, 10, dataSource,
|
BlackboardArtifact mock1 = TskMockUtils.getArtifact(ART_TYPE, 10, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 7),
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 7),
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", day));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", day));
|
||||||
|
|
||||||
BlackboardArtifact mock2 = TskMockUtils.mockArtifact(ART_TYPE, 20, dataSource,
|
BlackboardArtifact mock2 = TskMockUtils.getArtifact(ART_TYPE, 20, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 6));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 6));
|
||||||
|
|
||||||
BlackboardArtifact mock3 = TskMockUtils.mockArtifact(ART_TYPE, 30, dataSource,
|
BlackboardArtifact mock3 = TskMockUtils.getArtifact(ART_TYPE, 30, dataSource,
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 5),
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, "TEST SOURCE", 5),
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", 3 * day));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, "TEST SOURCE", 3 * day));
|
||||||
|
|
||||||
@ -375,9 +352,9 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTskCoreExceptionThrown() throws TskCoreException {
|
public void getArtifacts_tskCoreExceptionThrown() throws TskCoreException {
|
||||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||||
TskMockUtils.mockDataSource(1),
|
TskMockUtils.getDataSource(1),
|
||||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
0,
|
0,
|
||||||
@ -388,9 +365,9 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testThrowOnLessThan0() throws TskCoreException {
|
public void getArtifacts_throwOnLessThan0() throws TskCoreException {
|
||||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||||
TskMockUtils.mockDataSource(1),
|
TskMockUtils.getDataSource(1),
|
||||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
-1,
|
-1,
|
||||||
@ -401,9 +378,9 @@ public class GetArtifactsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyListReturned() throws TskCoreException {
|
public void getArtifacts_emptyListReturned() throws TskCoreException {
|
||||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||||
TskMockUtils.mockDataSource(1),
|
TskMockUtils.getDataSource(1),
|
||||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
0,
|
0,
|
||||||
@ -412,4 +389,76 @@ public class GetArtifactsTest {
|
|||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of an artifact.
|
||||||
|
*/
|
||||||
|
private interface GetAttrVal<T> {
|
||||||
|
/**
|
||||||
|
* A method for retrieving the value of an artifact.
|
||||||
|
* @param artifact The artifact.
|
||||||
|
* @param type The type of attribute.
|
||||||
|
* @return The value.
|
||||||
|
*/
|
||||||
|
T getOrNull(BlackboardArtifact artifact, BlackboardAttribute.Type type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void testNullAttrValue(String id, GetAttrVal<T> getter, ARTIFACT_TYPE artifactType,
|
||||||
|
ATTRIBUTE_TYPE attributeType, T nonNullVal)
|
||||||
|
throws TskCoreException {
|
||||||
|
|
||||||
|
BlackboardAttribute.Type attrType = new BlackboardAttribute.Type(attributeType);
|
||||||
|
BlackboardArtifact.Type artType = new BlackboardArtifact.Type(artifactType);
|
||||||
|
|
||||||
|
BlackboardArtifact noAttribute = TskMockUtils.getArtifact(artType, 1000,
|
||||||
|
TskMockUtils.getDataSource(1), new ArrayList<>());
|
||||||
|
|
||||||
|
T nullValue = getter.getOrNull(noAttribute, attrType);
|
||||||
|
Assert.assertNull(String.format("Expected function %s to return null when no attribute present", id), nullValue);
|
||||||
|
|
||||||
|
BlackboardArtifact hasAttribute = TskMockUtils.getArtifact(artType, 1000,
|
||||||
|
TskMockUtils.getDataSource(1), TskMockUtils.getAttribute(attributeType, nonNullVal));
|
||||||
|
|
||||||
|
T valueReceived = getter.getOrNull(hasAttribute, attrType);
|
||||||
|
|
||||||
|
Assert.assertEquals(String.format("%s did not return the same value present in the attribute", id), nonNullVal, valueReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getStringOrNull_handlesNull() throws TskCoreException {
|
||||||
|
testNullAttrValue("getStringOrNull", DataSourceInfoUtilities::getStringOrNull,
|
||||||
|
ARTIFACT_TYPE.TSK_ACCOUNT, ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, "Skype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getIntOrNull_handlesNull() throws TskCoreException {
|
||||||
|
testNullAttrValue("getIntOrNull", DataSourceInfoUtilities::getIntOrNull,
|
||||||
|
ARTIFACT_TYPE.TSK_PROG_RUN, ATTRIBUTE_TYPE.TSK_COUNT, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getLongOrNull_handlesNull() throws TskCoreException {
|
||||||
|
testNullAttrValue("getLongOrNull", DataSourceInfoUtilities::getLongOrNull,
|
||||||
|
ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT, ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, 1001L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getDateOrNull_handlesNull() throws TskCoreException {
|
||||||
|
BlackboardAttribute.Type attrType = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME);
|
||||||
|
BlackboardArtifact.Type artType = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING);
|
||||||
|
|
||||||
|
long dateTime = 24 * 60 * 60 * 42;
|
||||||
|
|
||||||
|
BlackboardArtifact noAttribute = TskMockUtils.getArtifact(artType, 1000,
|
||||||
|
TskMockUtils.getDataSource(1), new ArrayList<>());
|
||||||
|
|
||||||
|
Date nullValue = DataSourceInfoUtilities.getDateOrNull(noAttribute, attrType);
|
||||||
|
Assert.assertNull(nullValue);
|
||||||
|
|
||||||
|
BlackboardArtifact hasAttribute = TskMockUtils.getArtifact(artType, 1000,
|
||||||
|
TskMockUtils.getDataSource(1), TskMockUtils.getAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, dateTime));
|
||||||
|
|
||||||
|
Date curVal = DataSourceInfoUtilities.getDateOrNull(hasAttribute, attrType);
|
||||||
|
Assert.assertEquals(dateTime, curVal.getTime() / 1000);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common tools for mocking in data source summary.
|
||||||
|
*/
|
||||||
|
public final class DataSourceSummaryMockUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a pair of a mock SleuthkitCase and mock Blackboard.
|
||||||
|
*
|
||||||
|
* @param returnArr The return result when calling getArtifacts on the
|
||||||
|
* blackboard.
|
||||||
|
*
|
||||||
|
* @return The pair of a mock SleuthkitCase and mock Blackboard.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
static Pair<SleuthkitCase, Blackboard> getArtifactsTSKMock(List<BlackboardArtifact> returnArr) throws TskCoreException {
|
||||||
|
SleuthkitCase mockCase = mock(SleuthkitCase.class);
|
||||||
|
Blackboard mockBlackboard = mock(Blackboard.class);
|
||||||
|
when(mockCase.getBlackboard()).thenReturn(mockBlackboard);
|
||||||
|
when(mockBlackboard.getArtifacts(anyInt(), anyLong())).thenReturn(returnArr);
|
||||||
|
return Pair.of(mockCase, mockBlackboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataSourceSummaryMockUtils() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,681 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.datasourcesummary.datamodel;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||||
|
import org.sleuthkit.autopsy.testutils.RandomizationUtils;
|
||||||
|
import org.sleuthkit.autopsy.testutils.TskMockUtils;
|
||||||
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for RecentFilesSummaryTest
|
||||||
|
*/
|
||||||
|
public class RecentFilesSummaryTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for calling methods in RecentFilesSummary in a uniform
|
||||||
|
* manner.
|
||||||
|
*/
|
||||||
|
private interface RecentFilesMethod<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Means of acquiring data from a method in RecentFilesSummary.
|
||||||
|
*
|
||||||
|
* @param recentFilesSummary The RecentFilesSummary object.
|
||||||
|
* @param dataSource The datasource.
|
||||||
|
* @param count The number of items to retrieve.
|
||||||
|
*
|
||||||
|
* @return The method's return data.
|
||||||
|
*
|
||||||
|
* @throws SleuthkitCaseProviderException
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
List<T> fetch(RecentFilesSummary recentFilesSummary, DataSource dataSource, int count)
|
||||||
|
throws SleuthkitCaseProviderException, TskCoreException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final RecentFilesMethod<RecentFileDetails> RECENT_DOCS_FUNCT
|
||||||
|
= (summary, dataSource, count) -> summary.getRecentlyOpenedDocuments(dataSource, count);
|
||||||
|
|
||||||
|
private static final RecentFilesMethod<RecentDownloadDetails> RECENT_DOWNLOAD_FUNCT
|
||||||
|
= (summary, dataSource, count) -> summary.getRecentDownloads(dataSource, count);
|
||||||
|
|
||||||
|
private static final RecentFilesMethod<RecentAttachmentDetails> RECENT_ATTACHMENT_FUNCT
|
||||||
|
= (summary, dataSource, count) -> summary.getRecentAttachments(dataSource, count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If -1 count passed to method, should throw IllegalArgumentException.
|
||||||
|
*
|
||||||
|
* @param method The method to call.
|
||||||
|
* @param methodName The name of the metho
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
* @throws SleuthkitCaseProviderException
|
||||||
|
*/
|
||||||
|
private <T> void testNonPositiveCount_ThrowsError(RecentFilesMethod<T> method, String methodName)
|
||||||
|
throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(null);
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
|
||||||
|
try {
|
||||||
|
method.fetch(summary, dataSource, -1);
|
||||||
|
fail("Expected method " + methodName + " to fail on negative count.");
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
verify(casePair.getRight(),
|
||||||
|
never().description("Expected negative count for " + methodName + " to not call any methods in SleuthkitCase."))
|
||||||
|
.getArtifacts(anyInt(), anyLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentlyOpenedDocuments_nonPositiveCount_ThrowsError() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNonPositiveCount_ThrowsError(RECENT_DOCS_FUNCT, "getRecentlyOpenedDocuments");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentDownloads_nonPositiveCount_ThrowsError() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNonPositiveCount_ThrowsError(RECENT_DOWNLOAD_FUNCT, "getRecentDownloads");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentAttachments_nonPositiveCount_ThrowsError() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNonPositiveCount_ThrowsError(RECENT_ATTACHMENT_FUNCT, "getRecentAttachments");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that if no data source provided, an empty list is returned and
|
||||||
|
* SleuthkitCase isn't called.
|
||||||
|
*
|
||||||
|
* @param recentFilesMethod The method to call.
|
||||||
|
* @param methodName The name of the method
|
||||||
|
*
|
||||||
|
* @throws SleuthkitCaseProviderException
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private <T> void testNoDataSource_ReturnsEmptyList(RecentFilesMethod<T> recentFilesMethod, String methodName)
|
||||||
|
throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(null);
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
|
||||||
|
List<? extends T> items = recentFilesMethod.fetch(summary, null, 10);
|
||||||
|
Assert.assertNotNull("Expected method " + methodName + " to return an empty list.", items);
|
||||||
|
Assert.assertEquals("Expected method " + methodName + " to return an empty list.", 0, items.size());
|
||||||
|
verify(casePair.getRight(),
|
||||||
|
never().description("Expected null datasource for " + methodName + " to not call any methods in SleuthkitCase."))
|
||||||
|
.getArtifacts(anyInt(), anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentlyOpenedDocuments_noDataSource_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNoDataSource_ReturnsEmptyList(RECENT_DOCS_FUNCT, "getRecentlyOpenedDocuments");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentDownloads_noDataSource_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNoDataSource_ReturnsEmptyList(RECENT_DOWNLOAD_FUNCT, "getRecentDownloads");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentAttachments_noDataSource_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNonPositiveCount_ThrowsError(RECENT_ATTACHMENT_FUNCT, "getRecentAttachments");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If SleuthkitCase returns no results, an empty list is returned.
|
||||||
|
*
|
||||||
|
* @param recentFilesMethod The method to call.
|
||||||
|
* @param methodName The name of the method.
|
||||||
|
*
|
||||||
|
* @throws SleuthkitCaseProviderException
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private <T> void testNoReturnedResults_ReturnsEmptyList(RecentFilesMethod<T> recentFilesMethod, String methodName)
|
||||||
|
throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(Collections.emptyList());
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
List<? extends T> items = recentFilesMethod.fetch(summary, dataSource, 10);
|
||||||
|
Assert.assertNotNull("Expected method " + methodName + " to return an empty list.", items);
|
||||||
|
Assert.assertEquals("Expected method " + methodName + " to return an empty list.", 0, items.size());
|
||||||
|
verify(casePair.getRight(),
|
||||||
|
times(1).description("Expected " + methodName + " to call Blackboard once."))
|
||||||
|
.getArtifacts(anyInt(), anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentlyOpenedDocuments_noReturnedResults_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNoReturnedResults_ReturnsEmptyList(RECENT_DOCS_FUNCT, "getRecentlyOpenedDocuments");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentDownloads_noReturnedResults_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNoReturnedResults_ReturnsEmptyList(RECENT_DOWNLOAD_FUNCT, "getRecentDownloads");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentAttachments_testNoDataSource_ReturnsEmptyList() throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
|
testNoReturnedResults_ReturnsEmptyList(RECENT_ATTACHMENT_FUNCT, "getRecentAttachments");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long DAY_SECONDS = 24 * 60 * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A means of creating a number representing seconds from epoch where the
|
||||||
|
* lower the idx, the more recent the time.
|
||||||
|
*/
|
||||||
|
private static final Function<Integer, Long> dateTimeRetriever = (idx) -> (365 - idx) * DAY_SECONDS + 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a mock BlackboardArtifact.
|
||||||
|
*
|
||||||
|
* @param ds The data source to which the artifact belongs.
|
||||||
|
* @param artifactId The artifact id.
|
||||||
|
* @param artType The artifact type.
|
||||||
|
* @param attributeArgs The mapping of attribute type to value for each
|
||||||
|
* attribute in the artifact.
|
||||||
|
*
|
||||||
|
* @return The mock artifact.
|
||||||
|
*/
|
||||||
|
private BlackboardArtifact getArtifact(DataSource ds, long artifactId, ARTIFACT_TYPE artType, List<Pair<ATTRIBUTE_TYPE, Object>> attributeArgs) {
|
||||||
|
try {
|
||||||
|
List<BlackboardAttribute> attributes = attributeArgs.stream()
|
||||||
|
.filter((arg) -> arg != null && arg.getLeft() != null && arg.getRight() != null)
|
||||||
|
.map((arg) -> {
|
||||||
|
return TskMockUtils.getAttribute(arg.getLeft(), arg.getRight());
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return TskMockUtils.getArtifact(new BlackboardArtifact.Type(artType), artifactId, ds, attributes);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
fail("There was an error mocking an artifact.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mock artifact for getRecentlyOpenedDocuments.
|
||||||
|
*
|
||||||
|
* @param ds The datasource for the artifact.
|
||||||
|
* @param artifactId The artifact id.
|
||||||
|
* @param dateTime The time in seconds from epoch.
|
||||||
|
* @param path The path for the document.
|
||||||
|
*
|
||||||
|
* @return The mock artifact with pertinent attributes.
|
||||||
|
*/
|
||||||
|
private BlackboardArtifact getRecentDocumentArtifact(DataSource ds, long artifactId, Long dateTime, String path) {
|
||||||
|
return getArtifact(ds, artifactId, ARTIFACT_TYPE.TSK_RECENT_OBJECT, Arrays.asList(
|
||||||
|
Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME, dateTime),
|
||||||
|
Pair.of(ATTRIBUTE_TYPE.TSK_PATH, path)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentlyOpenedDocuments_sortedByDateTimeAndLimited() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
Function<Integer, String> pathRetriever = (idx) -> "/path/to/downloads/" + idx;
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
int countRequest = 10;
|
||||||
|
for (int countToGenerate : new int[]{1, 9, 10, 11}) {
|
||||||
|
// generate artifacts for each artifact
|
||||||
|
List<BlackboardArtifact> artifacts = new ArrayList<>();
|
||||||
|
for (int idx = 0; idx < countToGenerate; idx++) {
|
||||||
|
BlackboardArtifact artifact = getRecentDocumentArtifact(dataSource,
|
||||||
|
1000 + idx, dateTimeRetriever.apply(idx), pathRetriever.apply(idx));
|
||||||
|
artifacts.add(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
// run through method
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
List<RecentFileDetails> results = summary.getRecentlyOpenedDocuments(dataSource, countRequest);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
int expectedCount = Math.min(countRequest, countToGenerate);
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(expectedCount, results.size());
|
||||||
|
for (int i = 0; i < expectedCount; i++) {
|
||||||
|
Assert.assertEquals(dateTimeRetriever.apply(i), results.get(i).getDateAsLong());
|
||||||
|
Assert.assertEquals(pathRetriever.apply(i), results.get(i).getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentlyOpenedDocuments_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
BlackboardArtifact successItem = getRecentDocumentArtifact(dataSource, 1001, DAY_SECONDS, "/a/path");
|
||||||
|
BlackboardArtifact nullTime = getRecentDocumentArtifact(dataSource, 1002, null, "/a/path2");
|
||||||
|
BlackboardArtifact zeroTime = getRecentDocumentArtifact(dataSource, 10021, 0L, "/a/path2a");
|
||||||
|
List<BlackboardArtifact> artifacts = Arrays.asList(nullTime, zeroTime, successItem);
|
||||||
|
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
List<RecentFileDetails> results = summary.getRecentlyOpenedDocuments(dataSource, 10);
|
||||||
|
|
||||||
|
// verify results (only successItem)
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(1, results.size());
|
||||||
|
Assert.assertEquals((Long) DAY_SECONDS, results.get(0).getDateAsLong());
|
||||||
|
Assert.assertTrue("/a/path".equalsIgnoreCase(results.get(0).getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mock blackboard artifact for getRecentDownloads.
|
||||||
|
*
|
||||||
|
* @param ds The datasource.
|
||||||
|
* @param artifactId The artifact id.
|
||||||
|
* @param dateTime The time in seconds from epoch.
|
||||||
|
* @param domain The domain.
|
||||||
|
* @param path The path for the download.
|
||||||
|
*
|
||||||
|
* @return The mock artifact.
|
||||||
|
*/
|
||||||
|
private BlackboardArtifact getRecentDownloadArtifact(DataSource ds, long artifactId, Long dateTime, String domain, String path) {
|
||||||
|
return getArtifact(ds, artifactId, ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, Arrays.asList(
|
||||||
|
Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, dateTime),
|
||||||
|
Pair.of(ATTRIBUTE_TYPE.TSK_DOMAIN, domain),
|
||||||
|
Pair.of(ATTRIBUTE_TYPE.TSK_PATH, path)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentDownloads_sortedByDateTimeAndLimited() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
Function<Integer, String> domainRetriever = (idx) -> String.format("www.domain%d.com", idx);
|
||||||
|
Function<Integer, String> pathRetriever = (idx) -> "/path/to/downloads/doc" + idx + ".pdf";
|
||||||
|
|
||||||
|
// run through method
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
int countRequest = 10;
|
||||||
|
for (int countToGenerate : new int[]{1, 9, 10, 11}) {
|
||||||
|
// generate artifacts for each artifact
|
||||||
|
List<BlackboardArtifact> artifacts = new ArrayList<>();
|
||||||
|
for (int idx = 0; idx < countToGenerate; idx++) {
|
||||||
|
BlackboardArtifact artifact = getRecentDownloadArtifact(dataSource,
|
||||||
|
1000 + idx, dateTimeRetriever.apply(idx), domainRetriever.apply(idx),
|
||||||
|
pathRetriever.apply(idx));
|
||||||
|
|
||||||
|
artifacts.add(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call method
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
List<RecentDownloadDetails> results = summary.getRecentDownloads(dataSource, countRequest);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
int expectedCount = Math.min(countRequest, countToGenerate);
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(expectedCount, results.size());
|
||||||
|
for (int i = 0; i < expectedCount; i++) {
|
||||||
|
Assert.assertEquals(dateTimeRetriever.apply(i), results.get(i).getDateAsLong());
|
||||||
|
Assert.assertEquals(pathRetriever.apply(i), results.get(i).getPath());
|
||||||
|
Assert.assertEquals(domainRetriever.apply(i), results.get(i).getWebDomain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentDownloads_filtersMissingData() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
BlackboardArtifact successItem = getRecentDownloadArtifact(dataSource, 1001, DAY_SECONDS, "domain1.com", "/a/path1");
|
||||||
|
BlackboardArtifact nullTime = getRecentDownloadArtifact(dataSource, 1002, null, "domain2.com", "/a/path2");
|
||||||
|
BlackboardArtifact zeroTime = getRecentDownloadArtifact(dataSource, 10021, 0L, "domain2a.com", "/a/path2a");
|
||||||
|
List<BlackboardArtifact> artifacts = Arrays.asList(nullTime, zeroTime, successItem);
|
||||||
|
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = DataSourceSummaryMockUtils.getArtifactsTSKMock(RandomizationUtils.getMixedUp(artifacts));
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
|
||||||
|
// call method
|
||||||
|
List<RecentDownloadDetails> results = summary.getRecentDownloads(dataSource, 10);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(1, results.size());
|
||||||
|
Assert.assertEquals((Long) DAY_SECONDS, results.get(0).getDateAsLong());
|
||||||
|
Assert.assertTrue("/a/path1".equalsIgnoreCase(results.get(0).getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getRecentAttachments method has special setup conditions. This class
|
||||||
|
* encapsulates all the SleuthkitCase/BlackboardArtifact setup for on
|
||||||
|
* possible return item.
|
||||||
|
*/
|
||||||
|
private class AttachmentArtifactItem {
|
||||||
|
|
||||||
|
private final Integer messageArtifactTypeId;
|
||||||
|
private final boolean associatedAttrFormed;
|
||||||
|
private final String emailFrom;
|
||||||
|
private final Long messageTime;
|
||||||
|
private final boolean isParent;
|
||||||
|
private final String fileParentPath;
|
||||||
|
private final String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with all parameters.
|
||||||
|
*
|
||||||
|
* @param messageArtifactTypeId The type id for the artifact or null if
|
||||||
|
* no message artifact to be created.
|
||||||
|
* @param emailFrom Who the message is from or null not to
|
||||||
|
* include attribute.
|
||||||
|
* @param messageTime Time in seconds from epoch or null not
|
||||||
|
* to include attribute.
|
||||||
|
* @param fileParentPath The parent AbstractFile's path value.
|
||||||
|
* @param fileName The parent AbstractFile's filename
|
||||||
|
* value.
|
||||||
|
* @param associatedAttrFormed If false, the TSK_ASSOCIATED_OBJECT
|
||||||
|
* artifact has no attribute (even though
|
||||||
|
* it is required).
|
||||||
|
* @param hasParent Whether or not the artifact has a parent
|
||||||
|
* AbstractFile.
|
||||||
|
*/
|
||||||
|
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime,
|
||||||
|
String fileParentPath, String fileName,
|
||||||
|
boolean associatedAttrFormed, boolean hasParent) {
|
||||||
|
|
||||||
|
this.messageArtifactTypeId = messageArtifactTypeId;
|
||||||
|
this.associatedAttrFormed = associatedAttrFormed;
|
||||||
|
this.emailFrom = emailFrom;
|
||||||
|
this.messageTime = messageTime;
|
||||||
|
this.isParent = hasParent;
|
||||||
|
this.fileParentPath = fileParentPath;
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience constructor where defaults of required attributes and
|
||||||
|
* SleuthkitCase assumed.
|
||||||
|
*
|
||||||
|
* @param messageArtifactTypeId The type id for the artifact or null if
|
||||||
|
* no message artifact to be created.
|
||||||
|
* @param emailFrom Who the message is from or null not to
|
||||||
|
* include attribute.
|
||||||
|
* @param messageTime Time in seconds from epoch or null not
|
||||||
|
* to include attribute.
|
||||||
|
* @param fileParentPath The parent AbstractFile's path value.
|
||||||
|
* @param fileName The parent AbstractFile's filename
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
AttachmentArtifactItem(Integer messageArtifactTypeId, String emailFrom, Long messageTime, String fileParentPath, String fileName) {
|
||||||
|
this(messageArtifactTypeId, emailFrom, messageTime, fileParentPath, fileName, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAssociatedAttrFormed() {
|
||||||
|
return associatedAttrFormed;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getEmailFrom() {
|
||||||
|
return emailFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long getMessageTime() {
|
||||||
|
return messageTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasParent() {
|
||||||
|
return isParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFileParentPath() {
|
||||||
|
return fileParentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer getMessageArtifactTypeId() {
|
||||||
|
return messageArtifactTypeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the associated artifact message for the TSK_ASSOCIATED_OBJECT.
|
||||||
|
*
|
||||||
|
* @param artifacts The mapping of artifact id to artifact.
|
||||||
|
* @param item The record to setup.
|
||||||
|
* @param dataSource The datasource.
|
||||||
|
* @param associatedId The associated attribute id.
|
||||||
|
* @param artifactId The artifact id.
|
||||||
|
*
|
||||||
|
* @return The associated Artifact blackboard attribute.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private BlackboardAttribute setupAssociatedMessage(Map<Long, BlackboardArtifact> artifacts, AttachmentArtifactItem item,
|
||||||
|
DataSource dataSource, Long associatedId, Long artifactId) throws TskCoreException {
|
||||||
|
|
||||||
|
BlackboardAttribute associatedAttr = TskMockUtils.getAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, associatedId);
|
||||||
|
|
||||||
|
if (item.getMessageArtifactTypeId() == null) {
|
||||||
|
return associatedAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the artifact type or null if not found
|
||||||
|
ARTIFACT_TYPE messageType = Stream.of(ARTIFACT_TYPE.values())
|
||||||
|
.filter((artType) -> artType.getTypeID() == item.getMessageArtifactTypeId())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
// if there is a message type, create the artifact
|
||||||
|
if (messageType != null) {
|
||||||
|
List<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
|
if (item.getEmailFrom() != null) {
|
||||||
|
attributes.add(TskMockUtils.getAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_FROM, item.getEmailFrom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getMessageTime() != null) {
|
||||||
|
attributes.add(TskMockUtils.getAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_SENT, item.getMessageTime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts.put(associatedId, TskMockUtils.getArtifact(
|
||||||
|
new BlackboardArtifact.Type(messageType), artifactId, dataSource, attributes));
|
||||||
|
}
|
||||||
|
return associatedAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since getRecentAttachments does not simply query one type of artifact and
|
||||||
|
* return results, this method sets up a mock SleuthkitCase and Blackboard
|
||||||
|
* to return pertinent data.
|
||||||
|
*
|
||||||
|
* @param items Each attachment item where each item could represent a
|
||||||
|
* return result if fully formed.
|
||||||
|
*
|
||||||
|
* @return The mock SleuthkitCase and Blackboard.
|
||||||
|
*/
|
||||||
|
private Pair<SleuthkitCase, Blackboard> getRecentAttachmentArtifactCase(List<AttachmentArtifactItem> items) {
|
||||||
|
SleuthkitCase skCase = mock(SleuthkitCase.class);
|
||||||
|
Blackboard blackboard = mock(Blackboard.class);
|
||||||
|
when(skCase.getBlackboard()).thenReturn(blackboard);
|
||||||
|
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
long objIdCounter = 100;
|
||||||
|
Map<Long, BlackboardArtifact> artifacts = new HashMap<>();
|
||||||
|
try {
|
||||||
|
for (AttachmentArtifactItem item : items) {
|
||||||
|
BlackboardAttribute associatedAttr = null;
|
||||||
|
// if the associated attribute is fully formed,
|
||||||
|
// create the associated attribute and related artifact
|
||||||
|
if (item.isAssociatedAttrFormed()) {
|
||||||
|
associatedAttr = setupAssociatedMessage(artifacts, item, dataSource, ++objIdCounter, ++objIdCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the content parent for the associated object if one should be present
|
||||||
|
Content parent = (item.hasParent())
|
||||||
|
? TskMockUtils.getAbstractFile(++objIdCounter, item.getFileParentPath(), item.getFileName())
|
||||||
|
: null;
|
||||||
|
|
||||||
|
Long associatedId = ++objIdCounter;
|
||||||
|
artifacts.put(associatedId, TskMockUtils.getArtifact(
|
||||||
|
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT),
|
||||||
|
parent, associatedId, dataSource, associatedAttr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the blackboard to return artifacts that match the type id.
|
||||||
|
when(blackboard.getArtifacts(anyInt(), anyLong())).thenAnswer((inv) -> {
|
||||||
|
Object[] args = inv.getArguments();
|
||||||
|
int artifactType = (Integer) args[0];
|
||||||
|
return artifacts.values().stream()
|
||||||
|
.filter(art -> art.getArtifactTypeID() == artifactType)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
});
|
||||||
|
|
||||||
|
// also set up the sleuthkitcase to return the artifact with the matching id or null.
|
||||||
|
when(skCase.getBlackboardArtifact(anyLong())).thenAnswer((inv2) -> {
|
||||||
|
Object[] args2 = inv2.getArguments();
|
||||||
|
long id = (Long) args2[0];
|
||||||
|
return artifacts.get(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Pair.of(skCase, blackboard);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
fail("There was an error while creating SleuthkitCase for getRecentAttachments");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentAttachments_sortedByDateTimeAndLimited() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
// a deterministic means of transforming an index into a particular attribute type so that they can be created
|
||||||
|
// and compared on return
|
||||||
|
Function<Integer, String> emailFromRetriever = (idx) -> String.format("person%d@basistech.com", idx);
|
||||||
|
Function<Integer, String> pathRetriever = (idx) -> "/path/to/attachment/" + idx;
|
||||||
|
Function<Integer, String> fileNameRetriever = (idx) -> String.format("%d-filename.png", idx);
|
||||||
|
|
||||||
|
int countRequest = 10;
|
||||||
|
for (int countToGenerate : new int[]{1, 9, 10, 11}) {
|
||||||
|
// set up the items in the sleuthkit case
|
||||||
|
List<AttachmentArtifactItem> items = IntStream.range(0, countToGenerate)
|
||||||
|
.mapToObj((idx) -> new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||||
|
emailFromRetriever.apply(idx), dateTimeRetriever.apply(idx),
|
||||||
|
pathRetriever.apply(idx), fileNameRetriever.apply(idx)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<AttachmentArtifactItem> mixedUpItems = RandomizationUtils.getMixedUp(items);
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = getRecentAttachmentArtifactCase(mixedUpItems);
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
|
||||||
|
// retrieve results
|
||||||
|
List<RecentAttachmentDetails> results = summary.getRecentAttachments(dataSource, countRequest);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
int expectedCount = Math.min(countRequest, countToGenerate);
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(expectedCount, results.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedCount; i++) {
|
||||||
|
RecentAttachmentDetails result = results.get(i);
|
||||||
|
Assert.assertEquals(dateTimeRetriever.apply(i), result.getDateAsLong());
|
||||||
|
Assert.assertTrue(emailFromRetriever.apply(i).equalsIgnoreCase(result.getSender()));
|
||||||
|
Assert.assertTrue(Paths.get(pathRetriever.apply(i), fileNameRetriever.apply(i)).toString()
|
||||||
|
.equalsIgnoreCase(result.getPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRecentAttachments_filterData() throws SleuthkitCaseProviderException, TskCoreException {
|
||||||
|
// setup data
|
||||||
|
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||||
|
|
||||||
|
AttachmentArtifactItem successItem = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
|
"person@sleuthkit.com", DAY_SECONDS, "/parent/path", "msg.pdf");
|
||||||
|
AttachmentArtifactItem successItem2 = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||||
|
"person_on_skype", DAY_SECONDS + 1, "/parent/path/to/skype", "skype.png");
|
||||||
|
AttachmentArtifactItem wrongArtType = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
|
||||||
|
"5555675309", DAY_SECONDS + 2, "/path/to/callog/info", "callog.dat");
|
||||||
|
AttachmentArtifactItem missingTimeStamp = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
|
"person2@sleuthkit.com", null, "/parent/path", "msg2.pdf");
|
||||||
|
AttachmentArtifactItem zeroTimeStamp = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
|
"person2a@sleuthkit.com", 0L, "/parent/path", "msg2a.png");
|
||||||
|
AttachmentArtifactItem noParentFile = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
|
"person4@sleuthkit.com", DAY_SECONDS + 4, "/parent/path", "msg4.jpg", true, false);
|
||||||
|
AttachmentArtifactItem noAssocAttr = new AttachmentArtifactItem(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||||
|
"person3@sleuthkit.com", DAY_SECONDS + 5, "/parent/path", "msg5.gif", false, true);
|
||||||
|
AttachmentArtifactItem missingAssocArt = new AttachmentArtifactItem(null,
|
||||||
|
"person3@sleuthkit.com", DAY_SECONDS + 6, "/parent/path", "msg6.pdf");
|
||||||
|
|
||||||
|
List<AttachmentArtifactItem> items = Arrays.asList(successItem, successItem2,
|
||||||
|
wrongArtType, missingTimeStamp, zeroTimeStamp,
|
||||||
|
noParentFile, noAssocAttr, missingAssocArt);
|
||||||
|
|
||||||
|
Pair<SleuthkitCase, Blackboard> casePair = getRecentAttachmentArtifactCase(items);
|
||||||
|
RecentFilesSummary summary = new RecentFilesSummary(() -> casePair.getLeft());
|
||||||
|
|
||||||
|
// get data
|
||||||
|
List<RecentAttachmentDetails> results = summary.getRecentAttachments(dataSource, 10);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
Assert.assertNotNull(results);
|
||||||
|
Assert.assertEquals(2, results.size());
|
||||||
|
RecentAttachmentDetails successItem2Details = results.get(0);
|
||||||
|
RecentAttachmentDetails successItemDetails = results.get(1);
|
||||||
|
|
||||||
|
Assert.assertEquals(successItemDetails.getDateAsLong(), (Long) DAY_SECONDS);
|
||||||
|
Assert.assertTrue(Paths.get(successItem.getFileParentPath(), successItem.getFileName())
|
||||||
|
.toString().equalsIgnoreCase(successItemDetails.getPath()));
|
||||||
|
Assert.assertTrue(successItem.getEmailFrom().equalsIgnoreCase(successItemDetails.getSender()));
|
||||||
|
|
||||||
|
Assert.assertEquals(successItem2Details.getDateAsLong(), (Long) (DAY_SECONDS + 1));
|
||||||
|
Assert.assertTrue(Paths.get(successItem2.getFileParentPath(), successItem2.getFileName())
|
||||||
|
.toString().equalsIgnoreCase(successItem2Details.getPath()));
|
||||||
|
Assert.assertTrue(successItem2.getEmailFrom().equalsIgnoreCase(successItem2Details.getSender()));
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -32,7 +32,7 @@ public class DomainSearchTestUtils {
|
|||||||
|
|
||||||
public static ResultDomain mockDomainResult(String domain, long start, long end,
|
public static ResultDomain mockDomainResult(String domain, long start, long end,
|
||||||
long totalVisits, long visits, long filesDownloaded, long dataSourceId) {
|
long totalVisits, long visits, long filesDownloaded, long dataSourceId) {
|
||||||
Content dataSource = TskMockUtils.mockDataSource(dataSourceId);
|
Content dataSource = TskMockUtils.getDataSource(dataSourceId);
|
||||||
return new ResultDomain(domain, start, end, totalVisits,
|
return new ResultDomain(domain, start, end, totalVisits,
|
||||||
visits, filesDownloaded, dataSource);
|
visits, filesDownloaded, dataSource);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.testutils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tools for pseudo-randomization.
|
||||||
|
*/
|
||||||
|
public final class RandomizationUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list in 0, n-1, 1, n-2 ... order. Deterministic so same results
|
||||||
|
* each time, but not in original order.
|
||||||
|
*
|
||||||
|
* @return Mixed up list.
|
||||||
|
*/
|
||||||
|
public static <T> List<T> getMixedUp(List<T> list) {
|
||||||
|
int forward = 0;
|
||||||
|
int backward = list.size() - 1;
|
||||||
|
|
||||||
|
List<T> newList = new ArrayList<>();
|
||||||
|
while (forward <= backward) {
|
||||||
|
newList.add(list.get(forward));
|
||||||
|
|
||||||
|
if (forward < backward) {
|
||||||
|
newList.add(list.get(backward));
|
||||||
|
}
|
||||||
|
|
||||||
|
forward++;
|
||||||
|
backward--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RandomizationUtils() {
|
||||||
|
}
|
||||||
|
}
|
@ -19,15 +19,26 @@
|
|||||||
package org.sleuthkit.autopsy.testutils;
|
package org.sleuthkit.autopsy.testutils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.ConsoleHandler;
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
||||||
|
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||||
|
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -44,7 +55,7 @@ public class TskMockUtils {
|
|||||||
*
|
*
|
||||||
* @return The mocked datasource.
|
* @return The mocked datasource.
|
||||||
*/
|
*/
|
||||||
public static DataSource mockDataSource(long dataSourceId) {
|
public static DataSource getDataSource(long dataSourceId) {
|
||||||
DataSource dataSource = mock(DataSource.class);
|
DataSource dataSource = mock(DataSource.class);
|
||||||
when(dataSource.getName()).thenReturn("");
|
when(dataSource.getName()).thenReturn("");
|
||||||
when(dataSource.getId()).thenReturn(dataSourceId);
|
when(dataSource.getId()).thenReturn(dataSourceId);
|
||||||
@ -65,14 +76,35 @@ public class TskMockUtils {
|
|||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public static BlackboardArtifact mockArtifact(BlackboardArtifact.Type artifactType, long artifactId,
|
public static BlackboardArtifact getArtifact(BlackboardArtifact.Type artifactType, long artifactId,
|
||||||
|
DataSource dataSource, BlackboardAttribute... attributes) throws TskCoreException {
|
||||||
|
return getArtifact(artifactType, null, artifactId, dataSource, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a mock Blackboard artifact.
|
||||||
|
*
|
||||||
|
* @param artifactType The artifact type for the artifact.
|
||||||
|
* @param parent The parent file of the artifact.
|
||||||
|
* @param artifactId The artifact id.
|
||||||
|
* @param dataSource The datasource.
|
||||||
|
* @param attributes The attributes for the artifact.
|
||||||
|
*
|
||||||
|
* @return The mocked artifact.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
public static BlackboardArtifact getArtifact(BlackboardArtifact.Type artifactType, Content parent, long artifactId,
|
||||||
DataSource dataSource, BlackboardAttribute... attributes) throws TskCoreException {
|
DataSource dataSource, BlackboardAttribute... attributes) throws TskCoreException {
|
||||||
|
|
||||||
BlackboardArtifact artifact = mock(BlackboardArtifact.class);
|
BlackboardArtifact artifact = mock(BlackboardArtifact.class);
|
||||||
|
|
||||||
final Map<BlackboardAttribute.Type, BlackboardAttribute> attributeTypes = Stream.of(attributes)
|
final Map<BlackboardAttribute.Type, BlackboardAttribute> attributeTypes = Stream.of(attributes)
|
||||||
|
.filter(attr -> attr != null)
|
||||||
.collect(Collectors.toMap((attr) -> attr.getAttributeType(), Function.identity()));
|
.collect(Collectors.toMap((attr) -> attr.getAttributeType(), Function.identity()));
|
||||||
|
|
||||||
|
when(artifact.getParent()).thenReturn(parent);
|
||||||
|
|
||||||
when(artifact.getArtifactID()).thenReturn(artifactId);
|
when(artifact.getArtifactID()).thenReturn(artifactId);
|
||||||
|
|
||||||
when(artifact.getArtifactTypeID()).thenReturn(artifactType.getTypeID());
|
when(artifact.getArtifactTypeID()).thenReturn(artifactType.getTypeID());
|
||||||
@ -89,6 +121,146 @@ public class TskMockUtils {
|
|||||||
return artifact;
|
return artifact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BlackboardArtifact getArtifact(BlackboardArtifact.Type artifactType, long artifactId,
|
||||||
|
DataSource dataSource, List<BlackboardAttribute> attributes) throws TskCoreException {
|
||||||
|
|
||||||
|
return getArtifact(artifactType, artifactId, dataSource, attributes.toArray(new BlackboardAttribute[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String DEFAULT_ATTR_SOURCE = "TEST SOURCE";
|
||||||
|
|
||||||
|
public static BlackboardAttribute getAttribute(ATTRIBUTE_TYPE attrType, Object value) {
|
||||||
|
|
||||||
|
return getAttribute(new BlackboardAttribute.Type(attrType), DEFAULT_ATTR_SOURCE, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String source, Object value) {
|
||||||
|
switch (attrType.getValueType()) {
|
||||||
|
case STRING:
|
||||||
|
case JSON:
|
||||||
|
if (value instanceof String) {
|
||||||
|
return new BlackboardAttribute(attrType, source, (String) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DATETIME:
|
||||||
|
case LONG:
|
||||||
|
if (value instanceof Long) {
|
||||||
|
return new BlackboardAttribute(attrType, source, (Long) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
return new BlackboardAttribute(attrType, source, (Integer) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
if (value instanceof Double) {
|
||||||
|
return new BlackboardAttribute(attrType, source, (Double) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
if (value instanceof byte[]) {
|
||||||
|
return new BlackboardAttribute(attrType, source, (byte[]) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(String.format("Unknown attribute value type: %s", attrType.getValueType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(String.format("Attribute type expected type of %s but received argument of %s", attrType.getValueType(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mock TextTranslationService.
|
||||||
|
*
|
||||||
|
* @param onTranslate A function that performs the translation. If null, a
|
||||||
|
* null result is always returned for .translate method.
|
||||||
|
* @param hasProvider What to return for the hasProvider method.
|
||||||
|
*
|
||||||
|
* @return The mocked text translation service.
|
||||||
|
*
|
||||||
|
* @throws NoServiceProviderException
|
||||||
|
* @throws TranslationException
|
||||||
|
*/
|
||||||
|
public static TextTranslationService getTextTranslationService(Function<String, String> onTranslate, boolean hasProvider)
|
||||||
|
throws NoServiceProviderException, TranslationException {
|
||||||
|
TextTranslationService translationService = mock(TextTranslationService.class);
|
||||||
|
when(translationService.hasProvider()).thenReturn(hasProvider);
|
||||||
|
|
||||||
|
when(translationService.translate(anyString())).thenAnswer((invocation) -> {
|
||||||
|
if (onTranslate == null) {
|
||||||
|
throw new NoServiceProviderException("No onTranslate function provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] args = invocation.getArguments();
|
||||||
|
String input = (String) args[0];
|
||||||
|
return (input == null) ? null : onTranslate.apply(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
return translationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an AbstractFile mocking getPath and getName.
|
||||||
|
*
|
||||||
|
* @param objId The object id.
|
||||||
|
* @param path The path for the file.
|
||||||
|
* @param name The name
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static AbstractFile getAbstractFile(long objId, String path, String name) {
|
||||||
|
AbstractFile mocked = mock(AbstractFile.class);
|
||||||
|
when(mocked.getId()).thenReturn(objId);
|
||||||
|
when(mocked.getName()).thenReturn(name);
|
||||||
|
when(mocked.getParentPath()).thenReturn(path);
|
||||||
|
return mocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setConsoleHandler(Logger logger) {
|
||||||
|
// taken from https://stackoverflow.com/a/981230
|
||||||
|
// Handler for console (reuse it if it already exists)
|
||||||
|
Handler consoleHandler = null;
|
||||||
|
|
||||||
|
//see if there is already a console handler
|
||||||
|
for (Handler handler : logger.getHandlers()) {
|
||||||
|
if (handler instanceof ConsoleHandler) {
|
||||||
|
//found the console handler
|
||||||
|
consoleHandler = handler;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consoleHandler == null) {
|
||||||
|
//there was no console handler found, create a new one
|
||||||
|
consoleHandler = new ConsoleHandler();
|
||||||
|
logger.addHandler(consoleHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the console handler to fine:
|
||||||
|
consoleHandler.setLevel(java.util.logging.Level.FINEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an autopsy logger that does not write to disk.
|
||||||
|
*
|
||||||
|
* @param loggerName The name of the logger.
|
||||||
|
*
|
||||||
|
* @return The autopsy logger for the console
|
||||||
|
*
|
||||||
|
* @throws InstantiationException
|
||||||
|
* @throws IllegalStateException
|
||||||
|
*/
|
||||||
|
public static Logger getJavaLogger(String loggerName) {
|
||||||
|
// The logger doesn't appear to respond well to mocking with mockito.
|
||||||
|
// It appears that the issue may have to do with mocking methods in the java.* packages
|
||||||
|
// since the autopsy logger extends the java.util.logging.Logger class:
|
||||||
|
// https://javadoc.io/static/org.mockito/mockito-core/3.5.13/org/mockito/Mockito.html#39
|
||||||
|
Logger logger = Logger.getLogger(loggerName);
|
||||||
|
setConsoleHandler(logger);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
private TskMockUtils() {
|
private TskMockUtils() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2930,7 +2930,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
|||||||
String eventType = event.getPropertyName();
|
String eventType = event.getPropertyName();
|
||||||
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||||
synchronized (ingestLock) {
|
synchronized (ingestLock) {
|
||||||
ingestLock.notify();
|
if (! IngestManager.getInstance().isIngestRunning()) {
|
||||||
|
ingestLock.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
thirdparty/yara/ReadMe.txt
vendored
Executable file
39
thirdparty/yara/ReadMe.txt
vendored
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
This folder contains the projects you need for building and testing the yarabridge.dll and YaraJNIWrapper.jar.
|
||||||
|
|
||||||
|
bin:
|
||||||
|
Contains the built dll and jar.
|
||||||
|
|
||||||
|
yarabridge:
|
||||||
|
VS project to create the dll that wraps the the libyara library.
|
||||||
|
|
||||||
|
YaraJNIWrapper:
|
||||||
|
Simple jar file that contains the native JNI methods for accessing the yarabridge.dll.
|
||||||
|
|
||||||
|
|
||||||
|
Steps for building yarabridge, YaraJNIWrapper and YaraWrapperTest.
|
||||||
|
|
||||||
|
1. Clone the yara repo at the same level as you have the autopsy repo. https://github.com/VirusTotal/yara
|
||||||
|
2. Build libyara:
|
||||||
|
- Open the project yara/windows/2015/yara.sln
|
||||||
|
- Build Release x64.
|
||||||
|
3. Open the yarabridge project and build Release x64.
|
||||||
|
-If you have link issues, make sure you build release x64 in the previous step.
|
||||||
|
-This project will automatically copy the built dll to the bin folder.
|
||||||
|
4. Build YaraJNIWrapper
|
||||||
|
- Open in netbeans and select Build.
|
||||||
|
- Manually move the newly build jar file to the bin folder. After building the jar file can be found in
|
||||||
|
yara/YaraJNIWrapper/dist/
|
||||||
|
- Any matching rules will appear on the CL or the output of the project.
|
||||||
|
5. Test
|
||||||
|
- Open the YaraWrapperTest
|
||||||
|
- In the Run Properties you need to specify the path to a compiled yara rule file and a file to search.
|
||||||
|
There are sample files in YaraWrapperTest\resources.
|
||||||
|
- If you would like to make your own compiled rule file you can use the yarac tool that can be found
|
||||||
|
in yara/windows/vs2015/Release, if its not there go back to the yara project and build all of the
|
||||||
|
projects.
|
||||||
|
|
||||||
|
Troubleshooting:
|
||||||
|
- When building libyara make sure that you are building the vs2015 project (There is a vs2017 project too).
|
||||||
|
The paths in the yarabrige package are relative, but assume
|
||||||
|
that you are building the release version of the dll with the vs2015 project.
|
||||||
|
- Don't forget to move the YaraJNIWrapper.jar after you build it.
|
73
thirdparty/yara/YaraJNIWrapper/build.xml
vendored
Executable file
73
thirdparty/yara/YaraJNIWrapper/build.xml
vendored
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- You may freely edit this file. See commented blocks below for -->
|
||||||
|
<!-- some examples of how to customize the build. -->
|
||||||
|
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||||
|
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||||
|
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||||
|
<!-- the Compile on Save feature is turned off for the project. -->
|
||||||
|
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||||
|
<!-- in the project's Project Properties dialog box.-->
|
||||||
|
<project name="YaraJNIWrapper" default="default" basedir=".">
|
||||||
|
<description>Builds, tests, and runs the project YaraJNIWrapper.</description>
|
||||||
|
<import file="nbproject/build-impl.xml"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
There exist several targets which are by default empty and which can be
|
||||||
|
used for execution of your tasks. These targets are usually executed
|
||||||
|
before and after some main targets. They are:
|
||||||
|
|
||||||
|
-pre-init: called before initialization of project properties
|
||||||
|
-post-init: called after initialization of project properties
|
||||||
|
-pre-compile: called before javac compilation
|
||||||
|
-post-compile: called after javac compilation
|
||||||
|
-pre-compile-single: called before javac compilation of single file
|
||||||
|
-post-compile-single: called after javac compilation of single file
|
||||||
|
-pre-compile-test: called before javac compilation of JUnit tests
|
||||||
|
-post-compile-test: called after javac compilation of JUnit tests
|
||||||
|
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||||
|
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||||
|
-pre-jar: called before JAR building
|
||||||
|
-post-jar: called after JAR building
|
||||||
|
-post-clean: called after cleaning build products
|
||||||
|
|
||||||
|
(Targets beginning with '-' are not intended to be called on their own.)
|
||||||
|
|
||||||
|
Example of inserting an obfuscator after compilation could look like this:
|
||||||
|
|
||||||
|
<target name="-post-compile">
|
||||||
|
<obfuscate>
|
||||||
|
<fileset dir="${build.classes.dir}"/>
|
||||||
|
</obfuscate>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
For list of available properties check the imported
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
|
||||||
|
Another way to customize the build is by overriding existing main targets.
|
||||||
|
The targets of interest are:
|
||||||
|
|
||||||
|
-init-macrodef-javac: defines macro for javac compilation
|
||||||
|
-init-macrodef-junit: defines macro for junit execution
|
||||||
|
-init-macrodef-debug: defines macro for class debugging
|
||||||
|
-init-macrodef-java: defines macro for class execution
|
||||||
|
-do-jar: JAR building
|
||||||
|
run: execution of project
|
||||||
|
-javadoc-build: Javadoc generation
|
||||||
|
test-report: JUnit report generation
|
||||||
|
|
||||||
|
An example of overriding the target for project execution could look like this:
|
||||||
|
|
||||||
|
<target name="run" depends="YaraJNIWrapper-impl.jar">
|
||||||
|
<exec dir="bin" executable="launcher.exe">
|
||||||
|
<arg file="${dist.jar}"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
Notice that the overridden target depends on the jar target and not only on
|
||||||
|
the compile target as the regular run target does. Again, for a list of available
|
||||||
|
properties which you can use, check the target you are overriding in the
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
-->
|
||||||
|
</project>
|
1770
thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml
vendored
Executable file
1770
thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml
vendored
Executable file
File diff suppressed because it is too large
Load Diff
4
thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml
vendored
Executable file
4
thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml
vendored
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||||
|
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
|
||||||
|
</project-private>
|
93
thirdparty/yara/YaraJNIWrapper/nbproject/project.properties
vendored
Executable file
93
thirdparty/yara/YaraJNIWrapper/nbproject/project.properties
vendored
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
annotation.processing.enabled=true
|
||||||
|
annotation.processing.enabled.in.editor=false
|
||||||
|
annotation.processing.processor.options=
|
||||||
|
annotation.processing.processors.list=
|
||||||
|
annotation.processing.run.all.processors=true
|
||||||
|
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
|
||||||
|
build.classes.dir=${build.dir}/classes
|
||||||
|
build.classes.excludes=**/*.java,**/*.form
|
||||||
|
# This directory is removed when the project is cleaned:
|
||||||
|
build.dir=build
|
||||||
|
build.generated.dir=${build.dir}/generated
|
||||||
|
build.generated.sources.dir=${build.dir}/generated-sources
|
||||||
|
# Only compile against the classpath explicitly listed here:
|
||||||
|
build.sysclasspath=ignore
|
||||||
|
build.test.classes.dir=${build.dir}/test/classes
|
||||||
|
build.test.results.dir=${build.dir}/test/results
|
||||||
|
# Uncomment to specify the preferred debugger connection transport:
|
||||||
|
#debug.transport=dt_socket
|
||||||
|
debug.classpath=\
|
||||||
|
${run.classpath}
|
||||||
|
debug.modulepath=\
|
||||||
|
${run.modulepath}
|
||||||
|
debug.test.classpath=\
|
||||||
|
${run.test.classpath}
|
||||||
|
debug.test.modulepath=\
|
||||||
|
${run.test.modulepath}
|
||||||
|
# Files in build.classes.dir which should be excluded from distribution jar
|
||||||
|
dist.archive.excludes=
|
||||||
|
# This directory is removed when the project is cleaned:
|
||||||
|
dist.dir=dist
|
||||||
|
dist.jar=${dist.dir}/YaraJNIWrapper.jar
|
||||||
|
dist.javadoc.dir=${dist.dir}/javadoc
|
||||||
|
dist.jlink.dir=${dist.dir}/jlink
|
||||||
|
dist.jlink.output=${dist.jlink.dir}/YaraJNIWrapper
|
||||||
|
excludes=
|
||||||
|
includes=**
|
||||||
|
jar.compress=false
|
||||||
|
javac.classpath=
|
||||||
|
# Space-separated list of extra javac options
|
||||||
|
javac.compilerargs=
|
||||||
|
javac.deprecation=false
|
||||||
|
javac.external.vm=true
|
||||||
|
javac.modulepath=
|
||||||
|
javac.processormodulepath=
|
||||||
|
javac.processorpath=\
|
||||||
|
${javac.classpath}
|
||||||
|
javac.source=1.8
|
||||||
|
javac.target=1.8
|
||||||
|
javac.test.classpath=\
|
||||||
|
${javac.classpath}:\
|
||||||
|
${build.classes.dir}
|
||||||
|
javac.test.modulepath=\
|
||||||
|
${javac.modulepath}
|
||||||
|
javac.test.processorpath=\
|
||||||
|
${javac.test.classpath}
|
||||||
|
javadoc.additionalparam=
|
||||||
|
javadoc.author=false
|
||||||
|
javadoc.encoding=${source.encoding}
|
||||||
|
javadoc.html5=false
|
||||||
|
javadoc.noindex=false
|
||||||
|
javadoc.nonavbar=false
|
||||||
|
javadoc.notree=false
|
||||||
|
javadoc.private=false
|
||||||
|
javadoc.splitindex=true
|
||||||
|
javadoc.use=true
|
||||||
|
javadoc.version=false
|
||||||
|
javadoc.windowtitle=
|
||||||
|
# The jlink additional root modules to resolve
|
||||||
|
jlink.additionalmodules=
|
||||||
|
# The jlink additional command line parameters
|
||||||
|
jlink.additionalparam=
|
||||||
|
jlink.launcher=true
|
||||||
|
jlink.launcher.name=YaraJNIWrapper
|
||||||
|
meta.inf.dir=${src.dir}/META-INF
|
||||||
|
mkdist.disabled=true
|
||||||
|
platform.active=default_platform
|
||||||
|
run.classpath=\
|
||||||
|
${javac.classpath}:\
|
||||||
|
${build.classes.dir}
|
||||||
|
# Space-separated list of JVM arguments used when running the project.
|
||||||
|
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
|
||||||
|
# To set system properties for unit tests define test-sys-prop.name=value:
|
||||||
|
run.jvmargs=
|
||||||
|
run.modulepath=\
|
||||||
|
${javac.modulepath}
|
||||||
|
run.test.classpath=\
|
||||||
|
${javac.test.classpath}:\
|
||||||
|
${build.test.classes.dir}
|
||||||
|
run.test.modulepath=\
|
||||||
|
${javac.test.modulepath}
|
||||||
|
source.encoding=UTF-8
|
||||||
|
src.dir=src
|
||||||
|
test.src.dir=test
|
15
thirdparty/yara/YaraJNIWrapper/nbproject/project.xml
vendored
Executable file
15
thirdparty/yara/YaraJNIWrapper/nbproject/project.xml
vendored
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.java.j2seproject</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||||
|
<name>YaraJNIWrapper</name>
|
||||||
|
<source-roots>
|
||||||
|
<root id="src.dir"/>
|
||||||
|
</source-roots>
|
||||||
|
<test-roots>
|
||||||
|
<root id="test.src.dir"/>
|
||||||
|
</test-roots>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
68
thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java
vendored
Executable file
68
thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java
vendored
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.yara;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* native JNI interface to the yarabridge dll.
|
||||||
|
*/
|
||||||
|
public class YaraJNIWrapper {
|
||||||
|
|
||||||
|
// Load the yarabridge.dll which should be located in the same directory as
|
||||||
|
// the jar file. If we need to use this code for debugging the dll this
|
||||||
|
// code will need to be modified to add that support.
|
||||||
|
static {
|
||||||
|
Path directoryPath = null;
|
||||||
|
try {
|
||||||
|
directoryPath = Paths.get(YaraJNIWrapper.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().toAbsolutePath();
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
Logger.getLogger(YaraJNIWrapper.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
String libraryPath = Paths.get(directoryPath != null ? directoryPath.toString() : "", "yarabridge.dll").toAbsolutePath().toString();
|
||||||
|
System.load(libraryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of rules that were found in the given byteBuffer.
|
||||||
|
*
|
||||||
|
* The rule path must be to a yara compile rule file.
|
||||||
|
*
|
||||||
|
* @param compiledRulesPath
|
||||||
|
* @param byteBuffer
|
||||||
|
*
|
||||||
|
* @return List of rules found rules. Null maybe returned if error occurred.
|
||||||
|
*
|
||||||
|
* @throws YaraWrapperException
|
||||||
|
*/
|
||||||
|
static public native List<String> findRuleMatch(String compiledRulesPath, byte[] byteBuffer) throws YaraWrapperException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private constructor.
|
||||||
|
*/
|
||||||
|
private YaraJNIWrapper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraWrapperException.java
vendored
Executable file
37
thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraWrapperException.java
vendored
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.yara;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* An exception class for the YaraWrapper Library
|
||||||
|
*/
|
||||||
|
public class YaraWrapperException extends Exception{
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create exception containing the error message
|
||||||
|
*
|
||||||
|
* @param msg Error message
|
||||||
|
*/
|
||||||
|
public YaraWrapperException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
73
thirdparty/yara/YaraWrapperTest/build.xml
vendored
Executable file
73
thirdparty/yara/YaraWrapperTest/build.xml
vendored
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- You may freely edit this file. See commented blocks below for -->
|
||||||
|
<!-- some examples of how to customize the build. -->
|
||||||
|
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||||
|
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||||
|
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||||
|
<!-- the Compile on Save feature is turned off for the project. -->
|
||||||
|
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||||
|
<!-- in the project's Project Properties dialog box.-->
|
||||||
|
<project name="YaraWrapperTest" default="default" basedir=".">
|
||||||
|
<description>Builds, tests, and runs the project YaraWrapperTest.</description>
|
||||||
|
<import file="nbproject/build-impl.xml"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
There exist several targets which are by default empty and which can be
|
||||||
|
used for execution of your tasks. These targets are usually executed
|
||||||
|
before and after some main targets. They are:
|
||||||
|
|
||||||
|
-pre-init: called before initialization of project properties
|
||||||
|
-post-init: called after initialization of project properties
|
||||||
|
-pre-compile: called before javac compilation
|
||||||
|
-post-compile: called after javac compilation
|
||||||
|
-pre-compile-single: called before javac compilation of single file
|
||||||
|
-post-compile-single: called after javac compilation of single file
|
||||||
|
-pre-compile-test: called before javac compilation of JUnit tests
|
||||||
|
-post-compile-test: called after javac compilation of JUnit tests
|
||||||
|
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||||
|
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||||
|
-pre-jar: called before JAR building
|
||||||
|
-post-jar: called after JAR building
|
||||||
|
-post-clean: called after cleaning build products
|
||||||
|
|
||||||
|
(Targets beginning with '-' are not intended to be called on their own.)
|
||||||
|
|
||||||
|
Example of inserting an obfuscator after compilation could look like this:
|
||||||
|
|
||||||
|
<target name="-post-compile">
|
||||||
|
<obfuscate>
|
||||||
|
<fileset dir="${build.classes.dir}"/>
|
||||||
|
</obfuscate>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
For list of available properties check the imported
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
|
||||||
|
Another way to customize the build is by overriding existing main targets.
|
||||||
|
The targets of interest are:
|
||||||
|
|
||||||
|
-init-macrodef-javac: defines macro for javac compilation
|
||||||
|
-init-macrodef-junit: defines macro for junit execution
|
||||||
|
-init-macrodef-debug: defines macro for class debugging
|
||||||
|
-init-macrodef-java: defines macro for class execution
|
||||||
|
-do-jar: JAR building
|
||||||
|
run: execution of project
|
||||||
|
-javadoc-build: Javadoc generation
|
||||||
|
test-report: JUnit report generation
|
||||||
|
|
||||||
|
An example of overriding the target for project execution could look like this:
|
||||||
|
|
||||||
|
<target name="run" depends="YaraWrapperTest-impl.jar">
|
||||||
|
<exec dir="bin" executable="launcher.exe">
|
||||||
|
<arg file="${dist.jar}"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
Notice that the overridden target depends on the jar target and not only on
|
||||||
|
the compile target as the regular run target does. Again, for a list of available
|
||||||
|
properties which you can use, check the target you are overriding in the
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
-->
|
||||||
|
</project>
|
3
thirdparty/yara/YaraWrapperTest/manifest.mf
vendored
Executable file
3
thirdparty/yara/YaraWrapperTest/manifest.mf
vendored
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
Manifest-Version: 1.0
|
||||||
|
X-COMMENT: Main-Class will be added automatically by build
|
||||||
|
|
1770
thirdparty/yara/YaraWrapperTest/nbproject/build-impl.xml
vendored
Executable file
1770
thirdparty/yara/YaraWrapperTest/nbproject/build-impl.xml
vendored
Executable file
File diff suppressed because it is too large
Load Diff
4
thirdparty/yara/YaraWrapperTest/nbproject/private/private.xml
vendored
Executable file
4
thirdparty/yara/YaraWrapperTest/nbproject/private/private.xml
vendored
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||||
|
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
|
||||||
|
</project-private>
|
99
thirdparty/yara/YaraWrapperTest/nbproject/project.properties
vendored
Executable file
99
thirdparty/yara/YaraWrapperTest/nbproject/project.properties
vendored
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
annotation.processing.enabled=true
|
||||||
|
annotation.processing.enabled.in.editor=false
|
||||||
|
annotation.processing.processors.list=
|
||||||
|
annotation.processing.run.all.processors=true
|
||||||
|
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
|
||||||
|
application.title=YaraWrapperTest
|
||||||
|
application.vendor=kelly
|
||||||
|
build.classes.dir=${build.dir}/classes
|
||||||
|
build.classes.excludes=**/*.java,**/*.form
|
||||||
|
# This directory is removed when the project is cleaned:
|
||||||
|
build.dir=build
|
||||||
|
build.generated.dir=${build.dir}/generated
|
||||||
|
build.generated.sources.dir=${build.dir}/generated-sources
|
||||||
|
# Only compile against the classpath explicitly listed here:
|
||||||
|
build.sysclasspath=ignore
|
||||||
|
build.test.classes.dir=${build.dir}/test/classes
|
||||||
|
build.test.results.dir=${build.dir}/test/results
|
||||||
|
# Uncomment to specify the preferred debugger connection transport:
|
||||||
|
#debug.transport=dt_socket
|
||||||
|
debug.classpath=\
|
||||||
|
${run.classpath}
|
||||||
|
debug.modulepath=\
|
||||||
|
${run.modulepath}
|
||||||
|
debug.test.classpath=\
|
||||||
|
${run.test.classpath}
|
||||||
|
debug.test.modulepath=\
|
||||||
|
${run.test.modulepath}
|
||||||
|
# Files in build.classes.dir which should be excluded from distribution jar
|
||||||
|
dist.archive.excludes=
|
||||||
|
# This directory is removed when the project is cleaned:
|
||||||
|
dist.dir=dist
|
||||||
|
dist.jar=${dist.dir}/YaraWrapperTest.jar
|
||||||
|
dist.javadoc.dir=${dist.dir}/javadoc
|
||||||
|
dist.jlink.dir=${dist.dir}/jlink
|
||||||
|
dist.jlink.output=${dist.jlink.dir}/YaraWrapperTest
|
||||||
|
endorsed.classpath=
|
||||||
|
excludes=
|
||||||
|
file.reference.YaraJNIWrapper.jar=../bin/YaraJNIWrapper.jar
|
||||||
|
includes=**
|
||||||
|
jar.compress=false
|
||||||
|
javac.classpath=\
|
||||||
|
${file.reference.YaraJNIWrapper.jar}
|
||||||
|
# Space-separated list of extra javac options
|
||||||
|
javac.compilerargs=
|
||||||
|
javac.deprecation=false
|
||||||
|
javac.external.vm=true
|
||||||
|
javac.modulepath=
|
||||||
|
javac.processormodulepath=
|
||||||
|
javac.processorpath=\
|
||||||
|
${javac.classpath}
|
||||||
|
javac.source=1.8
|
||||||
|
javac.target=1.8
|
||||||
|
javac.test.classpath=\
|
||||||
|
${javac.classpath}:\
|
||||||
|
${build.classes.dir}
|
||||||
|
javac.test.modulepath=\
|
||||||
|
${javac.modulepath}
|
||||||
|
javac.test.processorpath=\
|
||||||
|
${javac.test.classpath}
|
||||||
|
javadoc.additionalparam=
|
||||||
|
javadoc.author=false
|
||||||
|
javadoc.encoding=${source.encoding}
|
||||||
|
javadoc.html5=false
|
||||||
|
javadoc.noindex=false
|
||||||
|
javadoc.nonavbar=false
|
||||||
|
javadoc.notree=false
|
||||||
|
javadoc.private=false
|
||||||
|
javadoc.splitindex=true
|
||||||
|
javadoc.use=true
|
||||||
|
javadoc.version=false
|
||||||
|
javadoc.windowtitle=
|
||||||
|
# The jlink additional root modules to resolve
|
||||||
|
jlink.additionalmodules=
|
||||||
|
# The jlink additional command line parameters
|
||||||
|
jlink.additionalparam=
|
||||||
|
jlink.launcher=true
|
||||||
|
jlink.launcher.name=YaraWrapperTest
|
||||||
|
main.class=org.sleuthkit.autopsy.yara.YaraWrapperTest
|
||||||
|
manifest.file=manifest.mf
|
||||||
|
meta.inf.dir=${src.dir}/META-INF
|
||||||
|
mkdist.disabled=false
|
||||||
|
platform.active=default_platform
|
||||||
|
run.classpath=\
|
||||||
|
${javac.classpath}:\
|
||||||
|
${build.classes.dir}
|
||||||
|
# Space-separated list of JVM arguments used when running the project.
|
||||||
|
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
|
||||||
|
# To set system properties for unit tests define test-sys-prop.name=value:
|
||||||
|
run.jvmargs=
|
||||||
|
run.modulepath=\
|
||||||
|
${javac.modulepath}
|
||||||
|
run.test.classpath=\
|
||||||
|
${javac.test.classpath}:\
|
||||||
|
${build.test.classes.dir}
|
||||||
|
run.test.modulepath=\
|
||||||
|
${javac.test.modulepath}
|
||||||
|
source.encoding=UTF-8
|
||||||
|
src.dir=src
|
||||||
|
test.src.dir=test
|
15
thirdparty/yara/YaraWrapperTest/nbproject/project.xml
vendored
Executable file
15
thirdparty/yara/YaraWrapperTest/nbproject/project.xml
vendored
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.java.j2seproject</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||||
|
<name>YaraWrapperTest</name>
|
||||||
|
<source-roots>
|
||||||
|
<root id="src.dir"/>
|
||||||
|
</source-roots>
|
||||||
|
<test-roots>
|
||||||
|
<root id="test.src.dir"/>
|
||||||
|
</test-roots>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
BIN
thirdparty/yara/YaraWrapperTest/resources/hello.compiled
vendored
Executable file
BIN
thirdparty/yara/YaraWrapperTest/resources/hello.compiled
vendored
Executable file
Binary file not shown.
1
thirdparty/yara/YaraWrapperTest/resources/hello.txt
vendored
Executable file
1
thirdparty/yara/YaraWrapperTest/resources/hello.txt
vendored
Executable file
@ -0,0 +1 @@
|
|||||||
|
Hello World
|
81
thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java
vendored
Executable file
81
thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java
vendored
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.yara;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.yara.YaraJNIWrapper;
|
||||||
|
import org.sleuthkit.autopsy.yara.YaraWrapperException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the YaraJNIWrapper code.
|
||||||
|
*/
|
||||||
|
public class YaraWrapperTest {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(YaraWrapperTest.class.getName());
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length < 2) {
|
||||||
|
System.out.println("Please supply two arguments, a yara compiled rule path and a path to the file to scan.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
testFileRuleMatch(args[0], args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the YaraJNIWrapper FindRuleMatch with the given path and output the
|
||||||
|
* results to the cl.
|
||||||
|
*
|
||||||
|
* @param compiledRulePath Path to yara compiled rule file
|
||||||
|
* @param filePath Path to file
|
||||||
|
*/
|
||||||
|
private static void testFileRuleMatch(String compiledRulePath, String filePath) {
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] data = Files.readAllBytes(path);
|
||||||
|
|
||||||
|
List<String> list = YaraJNIWrapper.findRuleMatch(compiledRulePath, data);
|
||||||
|
|
||||||
|
if (list != null) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
System.out.println("FindRuleMatch return an empty list");
|
||||||
|
} else {
|
||||||
|
System.out.println("Matching Rules:");
|
||||||
|
for (String s : list) {
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.log(Level.SEVERE, "FindRuleMatch return a null list");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException | YaraWrapperException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error thrown from yarabridge", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
thirdparty/yara/bin/YaraJNIWrapper.jar
vendored
Executable file
BIN
thirdparty/yara/bin/YaraJNIWrapper.jar
vendored
Executable file
Binary file not shown.
BIN
thirdparty/yara/bin/yarabridge.dll
vendored
Executable file
BIN
thirdparty/yara/bin/yarabridge.dll
vendored
Executable file
Binary file not shown.
28
thirdparty/yara/yarabridge/yarabridge.sln
vendored
Executable file
28
thirdparty/yara/yarabridge/yarabridge.sln
vendored
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 14
|
||||||
|
VisualStudioVersion = 14.0.25420.1
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yarabridge", "yarabridge\yarabridge.vcxproj", "{7922D123-F27A-427B-B3EF-964F5E79FDDA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Release|x64.Build.0 = Release|x64
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{7922D123-F27A-427B-B3EF-964F5E79FDDA}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
138
thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp
vendored
Executable file
138
thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp
vendored
Executable file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
** YaraBridge
|
||||||
|
**
|
||||||
|
** Brian Carrier [carrier <at> sleuthkit [dot] org]
|
||||||
|
** Copyright (c) 2010-2018 Brian Carrier. All Rights reserved
|
||||||
|
**
|
||||||
|
** This software is distributed under the Common Public License 1.0
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<jni.h>
|
||||||
|
#include "YaraJNIWrapper.h"
|
||||||
|
#include "yara.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Callback method to be passed to yr_rules_scan_mem method.
|
||||||
|
user_data is expected to be a pointer to a string vector.
|
||||||
|
*/
|
||||||
|
static int callback(
|
||||||
|
YR_SCAN_CONTEXT* context,
|
||||||
|
int message,
|
||||||
|
void* message_data,
|
||||||
|
void* user_data)
|
||||||
|
{
|
||||||
|
if (message == CALLBACK_MSG_RULE_MATCHING) {
|
||||||
|
YR_RULE *rule = (YR_RULE *)message_data;
|
||||||
|
|
||||||
|
((std::vector<std::string>*)user_data)->push_back(rule->identifier);
|
||||||
|
}
|
||||||
|
return CALLBACK_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Throw a new instance of YaraWrapperException with the given message.
|
||||||
|
|
||||||
|
Unlike in JAVA throwing this exception will not stop the execution
|
||||||
|
of the method from which it is thrown.
|
||||||
|
*/
|
||||||
|
static void throwException(JNIEnv *env, char * msg) {
|
||||||
|
jclass cls;
|
||||||
|
|
||||||
|
cls = env->FindClass("org/sleuthkit/autopsy/yara/YaraWrapperException");
|
||||||
|
if (cls == NULL) {
|
||||||
|
fprintf(stderr, "Failed to throw YaraWrapperException, cannot find class\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->ThrowNew(cls, msg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generic method that will create a Java ArrayList object populating it with
|
||||||
|
the strings from the given vector.
|
||||||
|
*/
|
||||||
|
jobject createArrayList(JNIEnv *env, std::vector<std::string> vector) {
|
||||||
|
jclass cls_arrayList = env->FindClass("java/util/ArrayList");
|
||||||
|
jmethodID constructor = env->GetMethodID(cls_arrayList, "<init>", "(I)V");
|
||||||
|
jmethodID method_add = env->GetMethodID(cls_arrayList, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
|
||||||
|
jobject list = env->NewObject(cls_arrayList, constructor, vector.size());
|
||||||
|
|
||||||
|
for (std::string str : vector) {
|
||||||
|
jstring element = env->NewStringUTF(str.c_str());
|
||||||
|
env->CallBooleanMethod(list, method_add, element);
|
||||||
|
env->DeleteLocalRef(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_sleuthkit_autopsy_yara_YaraJNIWrapper
|
||||||
|
* Method: FindRuleMatch
|
||||||
|
* Signature: (Ljava/lang/String;[B)Ljava/util/List;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatch
|
||||||
|
(JNIEnv * env, jclass cls, jstring compiledRulePath, jbyteArray fileByteArray) {
|
||||||
|
|
||||||
|
char errorMessage[256];
|
||||||
|
const char *nativeString = env->GetStringUTFChars(compiledRulePath, 0);
|
||||||
|
jobject resultList = NULL;
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if ((result = yr_initialize()) != ERROR_SUCCESS) {
|
||||||
|
sprintf_s(errorMessage, "libyara initialization error (%d)\n", result);
|
||||||
|
throwException(env, errorMessage);
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
YR_RULES *rules = NULL;
|
||||||
|
if ((result = yr_rules_load(nativeString, &rules)) != ERROR_SUCCESS) {
|
||||||
|
sprintf_s(errorMessage, "Failed to load compiled yara rules (%d)\n", result);
|
||||||
|
throwException(env, errorMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCopy;
|
||||||
|
int byteArrayLength = env->GetArrayLength(fileByteArray);
|
||||||
|
if (byteArrayLength == 0) {
|
||||||
|
throwException(env, "Unable to scan for matches. File byte array size was 0.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyte* nativeByteArray = env->GetByteArrayElements(fileByteArray, &isCopy);
|
||||||
|
int flags = 0;
|
||||||
|
std::vector<std::string> scanResults;
|
||||||
|
|
||||||
|
result = yr_rules_scan_mem(rules, (unsigned char*)nativeByteArray, byteArrayLength, flags, callback, &scanResults, 1000000);
|
||||||
|
env->ReleaseByteArrayElements(fileByteArray, nativeByteArray, 0);
|
||||||
|
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
sprintf_s(errorMessage, "Yara file scan failed (%d)\n", result);
|
||||||
|
throwException(env, errorMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultList = createArrayList(env, scanResults);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(compiledRulePath, nativeString);
|
||||||
|
yr_finalize();
|
||||||
|
|
||||||
|
return resultList;
|
||||||
|
|
||||||
|
}
|
21
thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h
vendored
Executable file
21
thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h
vendored
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class org_sleuthkit_autopsy_yara_YaraJNIWrapper */
|
||||||
|
|
||||||
|
#ifndef _Included_org_sleuthkit_autopsy_yara_YaraJNIWrapper
|
||||||
|
#define _Included_org_sleuthkit_autopsy_yara_YaraJNIWrapper
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: org_sleuthkit_autopsy_yara_YaraJNIWrapper
|
||||||
|
* Method: FindRuleMatch
|
||||||
|
* Signature: (Ljava/lang/String;[B)Ljava/util/List;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatch
|
||||||
|
(JNIEnv *, jclass, jstring, jbyteArray);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
8
thirdparty/yara/yarabridge/yarabridge/stdafx.cpp
vendored
Executable file
8
thirdparty/yara/yarabridge/yarabridge/stdafx.cpp
vendored
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
// stdafx.cpp : source file that includes just the standard includes
|
||||||
|
// yarabridge.pch will be the pre-compiled header
|
||||||
|
// stdafx.obj will contain the pre-compiled type information
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
// TODO: reference any additional headers you need in STDAFX.H
|
||||||
|
// and not in this file
|
16
thirdparty/yara/yarabridge/yarabridge/stdafx.h
vendored
Executable file
16
thirdparty/yara/yarabridge/yarabridge/stdafx.h
vendored
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
// stdafx.h : include file for standard system include files,
|
||||||
|
// or project specific include files that are used frequently, but
|
||||||
|
// are changed infrequently
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "targetver.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
// Windows Header Files:
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: reference additional headers your program requires here
|
8
thirdparty/yara/yarabridge/yarabridge/targetver.h
vendored
Executable file
8
thirdparty/yara/yarabridge/yarabridge/targetver.h
vendored
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||||
|
|
||||||
|
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||||
|
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||||
|
|
||||||
|
#include <SDKDDKVer.h>
|
174
thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj
vendored
Executable file
174
thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj
vendored
Executable file
@ -0,0 +1,174 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{7922D123-F27A-427B-B3EF-964F5E79FDDA}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>yarabridge</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IncludePath>$(JDK_HOME)\include\win32;$(JDK_HOME)\include;..\..\..\..\..\yara\libyara\include;$(IncludePath)</IncludePath>
|
||||||
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IncludePath>$(JDK_HOME)\include\win32;$(JDK_HOME)\include;..\..\..\..\..\yara\libyara\include;$(IncludePath)</IncludePath>
|
||||||
|
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;YARABRIDGE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;YARABRIDGE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\..\..\yara\windows\vs2015\libyara\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;crypt32.lib;libyara64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\bin\$(ProjectName).dll"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;YARABRIDGE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;YARABRIDGE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(JDK_HOME)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\..\..\yara\windows\vs2015\libyara\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;crypt32.lib;libyara64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\bin\$(ProjectName).dll"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Text Include="ReadMe.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="YaraJNIWrapper.h" />
|
||||||
|
<ClInclude Include="stdafx.h" />
|
||||||
|
<ClInclude Include="targetver.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="stdafx.cpp" />
|
||||||
|
<ClCompile Include="YaraJNIWrapper.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
@ -23,9 +23,11 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -79,7 +81,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
||||||
private Case currentCase;
|
private Case currentCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty constructor.
|
* Empty constructor.
|
||||||
*/
|
*/
|
||||||
@ -152,7 +154,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMbox) {
|
if (isMbox) {
|
||||||
return processMBox(abstractFile);
|
return processMBox(abstractFile);
|
||||||
}
|
}
|
||||||
@ -168,7 +170,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
if (isVcardFile) {
|
if (isVcardFile) {
|
||||||
return processVcard(abstractFile);
|
return processVcard(abstractFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,12 +213,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
PstParser parser = new PstParser(services);
|
PstParser parser = new PstParser(services);
|
||||||
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
||||||
|
|
||||||
|
|
||||||
switch( result) {
|
switch( result) {
|
||||||
case OK:
|
case OK:
|
||||||
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
||||||
if (pstMsgIterator != null) {
|
if (pstMsgIterator != null) {
|
||||||
processEmails(parser.getPartialEmailMessages(), pstMsgIterator , abstractFile);
|
processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile);
|
||||||
if (context.fileIngestIsCancelled()) {
|
if (context.fileIngestIsCancelled()) {
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
@ -272,7 +275,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
if (file.delete() == false) {
|
if (file.delete() == false) {
|
||||||
logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
|
logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,7 +454,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
List<AbstractFile> derivedFiles = new ArrayList<>();
|
List<AbstractFile> derivedFiles = new ArrayList<>();
|
||||||
|
|
||||||
BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile);
|
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||||
|
BlackboardArtifact msgArtifact = addEmailArtifact(message, abstractFile, accountFileInstanceCache);
|
||||||
|
accountFileInstanceCache.clear();
|
||||||
|
|
||||||
if ((msgArtifact != null) && (message.hasAttachment())) {
|
if ((msgArtifact != null) && (message.hasAttachment())) {
|
||||||
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
|
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
|
||||||
@ -527,7 +532,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
* @param fullMessageIterator
|
* @param fullMessageIterator
|
||||||
* @param abstractFile
|
* @param abstractFile
|
||||||
*/
|
*/
|
||||||
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator, AbstractFile abstractFile) {
|
private void processEmails(List<EmailMessage> partialEmailsForThreading, Iterator<EmailMessage> fullMessageIterator,
|
||||||
|
AbstractFile abstractFile) {
|
||||||
|
|
||||||
|
// Create cache for accounts
|
||||||
|
AccountFileInstanceCache accountFileInstanceCache = new AccountFileInstanceCache(abstractFile, currentCase);
|
||||||
|
|
||||||
// Putting try/catch around this to catch any exception and still allow
|
// Putting try/catch around this to catch any exception and still allow
|
||||||
// the creation of the artifacts to continue.
|
// the creation of the artifacts to continue.
|
||||||
@ -560,7 +569,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile);
|
BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile, accountFileInstanceCache);
|
||||||
|
|
||||||
if ((msgArtifact != null) && (current.hasAttachment())) {
|
if ((msgArtifact != null) && (current.hasAttachment())) {
|
||||||
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
|
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
|
||||||
@ -676,7 +685,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
* @return The generated e-mail message artifact.
|
* @return The generated e-mail message artifact.
|
||||||
*/
|
*/
|
||||||
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
|
||||||
private BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile) {
|
private BlackboardArtifact addEmailArtifact(EmailMessage email, AbstractFile abstractFile, AccountFileInstanceCache accountFileInstanceCache) {
|
||||||
BlackboardArtifact bbart = null;
|
BlackboardArtifact bbart = null;
|
||||||
List<BlackboardAttribute> bbattributes = new ArrayList<>();
|
List<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||||
String to = email.getRecipients();
|
String to = email.getRecipients();
|
||||||
@ -706,7 +715,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
if (senderAddressList.size() == 1) {
|
if (senderAddressList.size() == 1) {
|
||||||
senderAddress = senderAddressList.get(0);
|
senderAddress = senderAddressList.get(0);
|
||||||
try {
|
try {
|
||||||
senderAccountInstance = currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
|
senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
|
||||||
}
|
}
|
||||||
catch(TskCoreException ex) {
|
catch(TskCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
||||||
@ -731,9 +740,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
AccountFileInstance recipientAccountInstance =
|
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
|
||||||
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
|
|
||||||
EmailParserModuleFactory.getModuleName(), abstractFile);
|
|
||||||
recipientAccountInstances.add(recipientAccountInstance);
|
recipientAccountInstances.add(recipientAccountInstance);
|
||||||
}
|
}
|
||||||
catch(TskCoreException ex) {
|
catch(TskCoreException ex) {
|
||||||
@ -835,6 +842,56 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for storing AccountFileInstance.
|
||||||
|
* The idea is that emails will be used multiple times in a file and
|
||||||
|
* we shouldn't do a database lookup each time.
|
||||||
|
*/
|
||||||
|
static private class AccountFileInstanceCache {
|
||||||
|
private final Map<String, AccountFileInstance> cacheMap;
|
||||||
|
private final AbstractFile file;
|
||||||
|
private final Case currentCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new cache. Caches are linked to a specific file.
|
||||||
|
* @param file
|
||||||
|
* @param currentCase
|
||||||
|
*/
|
||||||
|
AccountFileInstanceCache(AbstractFile file, Case currentCase) {
|
||||||
|
cacheMap= new HashMap<>();
|
||||||
|
this.file = file;
|
||||||
|
this.currentCase = currentCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the account file instance from the cache or the database.
|
||||||
|
*
|
||||||
|
* @param email The email for this account.
|
||||||
|
*
|
||||||
|
* @return The corresponding AccountFileInstance
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
AccountFileInstance getAccountInstance(String email) throws TskCoreException {
|
||||||
|
if (cacheMap.containsKey(email)) {
|
||||||
|
return cacheMap.get(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountFileInstance accountInstance =
|
||||||
|
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, email,
|
||||||
|
EmailParserModuleFactory.getModuleName(), file);
|
||||||
|
cacheMap.put(email, accountInstance);
|
||||||
|
return accountInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the cache.
|
||||||
|
*/
|
||||||
|
void clear() {
|
||||||
|
cacheMap.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post an error message for the user.
|
* Post an error message for the user.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user