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"/>
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist">
|
||||
<mkdir dir="${ext.dir}"/>
|
||||
<copy file="${thirdparty.dir}/LICENSE-2.0.txt" todir="${ext.dir}" />
|
||||
@ -195,5 +195,47 @@
|
||||
<globmapper from="*" to="*-MERGED"/>
|
||||
</copy>
|
||||
</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>
|
||||
|
@ -96,7 +96,7 @@ Metadata.tableRowTitle.mimeType=MIME Type
|
||||
Metadata.tableRowTitle.name=Name
|
||||
Metadata.tableRowTitle.sectorSize=Sector Size
|
||||
Metadata.tableRowTitle.sha1=SHA1
|
||||
Metadata.tableRowTitle.sha256=SHA256
|
||||
Metadata.tableRowTitle.sha256=SHA-256
|
||||
Metadata.tableRowTitle.size=Size
|
||||
Metadata.tableRowTitle.fileNameAlloc=File Name 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.nodeText.truncated=(results truncated)",
|
||||
"Metadata.tableRowTitle.sha1=SHA1",
|
||||
"Metadata.tableRowTitle.sha256=SHA256",
|
||||
"Metadata.tableRowTitle.sha256=SHA-256",
|
||||
"Metadata.tableRowTitle.imageType=Type",
|
||||
"Metadata.tableRowTitle.sectorSize=Sector Size",
|
||||
"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");
|
||||
}
|
||||
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());
|
||||
addAcquisitionDetails(sb, dataSource);
|
||||
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
@ -97,6 +98,9 @@ final class PDFViewer implements FileTypeViewer {
|
||||
|
||||
// Add the IcePDF view to the center of our container.
|
||||
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
|
||||
// populate the model in the background.
|
||||
@ -122,12 +126,13 @@ final class PDFViewer implements FileTypeViewer {
|
||||
// will cause UI widgets to be updated.
|
||||
try {
|
||||
Document doc = get();
|
||||
controller.openDocument(doc, null);
|
||||
controller.openDocument(doc, file.getName());
|
||||
// This makes the PDF viewer appear as one continuous
|
||||
// document, which is the default for most popular PDF viewers.
|
||||
controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
|
||||
// This makes it possible to select text by left clicking and dragging.
|
||||
controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
|
||||
enableComponents(container, true);
|
||||
} catch (InterruptedException ex) {
|
||||
// Do nothing.
|
||||
} catch (ExecutionException ex) {
|
||||
@ -140,10 +145,28 @@ final class PDFViewer implements FileTypeViewer {
|
||||
file.getId(), file.getName()), ex);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public Component getComponent() {
|
||||
|
@ -278,6 +278,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
|
||||
"AbstractAbstractFileNode.knownColLbl=Known",
|
||||
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
|
||||
"AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash",
|
||||
"AbstractAbstractFileNode.objectId=Object ID",
|
||||
"AbstractAbstractFileNode.mimeType=MIME Type",
|
||||
"AbstractAbstractFileNode.extensionColLbl=Extension"})
|
||||
@ -305,6 +306,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
|
||||
KNOWN(AbstractAbstractFileNode_knownColLbl()),
|
||||
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
|
||||
SHA256HASH(AbstractAbstractFileNode_sha256HashColLbl()),
|
||||
ObjectID(AbstractAbstractFileNode_objectId()),
|
||||
MIMETYPE(AbstractAbstractFileNode_mimeType()),
|
||||
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<>(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<>(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<>(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(KNOWN.toString(), content.getKnown().getName());
|
||||
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(EXTENSION.toString(), content.getNameExtension());
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time
|
||||
AbstractAbstractFileNode.nameColLbl=Name
|
||||
AbstractAbstractFileNode.objectId=Object ID
|
||||
AbstractAbstractFileNode.originalName=Original Name
|
||||
AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash
|
||||
AbstractAbstractFileNode.sizeColLbl=Size
|
||||
AbstractAbstractFileNode.tagsProperty.displayName=Tags
|
||||
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
|
||||
|
@ -529,9 +529,11 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
ResultSet rs = results.getResultSet();) {
|
||||
List<Long> tempList = new ArrayList<>();
|
||||
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) {
|
||||
LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
|
||||
}
|
||||
|
@ -398,6 +398,20 @@ final class DataSourceInfoUtilities {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
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
|
||||
|
@ -118,7 +118,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
dataSource,
|
||||
DATETIME_ATT,
|
||||
DataSourceInfoUtilities.SortOrder.DESCENDING,
|
||||
10);
|
||||
maxCount);
|
||||
|
||||
List<RecentFileDetails> fileDetails = new ArrayList<>();
|
||||
for (BlackboardArtifact artifact : artifactList) {
|
||||
@ -134,12 +134,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
|
||||
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;
|
||||
@ -190,7 +189,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
path = attribute.getValueString();
|
||||
}
|
||||
}
|
||||
if (accessedTime != null) {
|
||||
if (accessedTime != null && accessedTime != 0L) {
|
||||
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
|
||||
}
|
||||
}
|
||||
@ -215,6 +214,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -241,7 +244,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
||||
}
|
||||
|
||||
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
|
||||
if (isMessageArtifact(messageArtifact)) {
|
||||
if (messageArtifact != null && isMessageArtifact(messageArtifact)) {
|
||||
Content content = artifact.getParent();
|
||||
if (content instanceof AbstractFile) {
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -30,6 +31,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -54,6 +56,36 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
*/
|
||||
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_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_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_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<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(
|
||||
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
|
||||
ARTIFACT_TYPE.TSK_CALLLOG.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"));
|
||||
@ -539,6 +605,189 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
.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.
|
||||
*/
|
||||
@ -722,4 +971,57 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
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.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||
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.TopDeviceAttachedResult;
|
||||
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.TopProgramsResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
@ -227,35 +226,30 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
private final TopProgramsSummary topProgramsData;
|
||||
|
||||
private final UserActivitySummary userActivityData;
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*/
|
||||
public UserActivityPanel() {
|
||||
this(new TopProgramsSummary(), new UserActivitySummary());
|
||||
this(new UserActivitySummary());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*
|
||||
* @param topProgramsData Class from which to obtain top programs data.
|
||||
* @param userActivityData Class from which to obtain remaining user
|
||||
* activity data.
|
||||
*/
|
||||
public UserActivityPanel(
|
||||
TopProgramsSummary topProgramsData,
|
||||
UserActivitySummary userActivityData) {
|
||||
|
||||
super(topProgramsData, userActivityData);
|
||||
|
||||
this.topProgramsData = topProgramsData;
|
||||
public UserActivityPanel(UserActivitySummary userActivityData) {
|
||||
super(userActivityData);
|
||||
this.userActivityData = userActivityData;
|
||||
|
||||
// set up data acquisition methods
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// top programs query
|
||||
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
|
||||
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
|
||||
(result) -> {
|
||||
showResultWithModuleCheck(topProgramsTable, result,
|
||||
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
|
||||
@ -307,7 +301,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
* @return The underlying short folder name if one exists.
|
||||
*/
|
||||
private String getShortFolderName(String path, String appName) {
|
||||
return this.topProgramsData.getShortFolderName(path, appName);
|
||||
return this.userActivityData.getShortFolderName(path, appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,6 +49,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
|
||||
FileSearchPanel.searchButton.text=Search
|
||||
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
||||
HashSearchPanel.md5CheckBox.text=MD5:
|
||||
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
||||
FileSearchPanel.errorLabel.text=\
|
||||
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.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
||||
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.wrongCharacter=MD5 contains invalid hex characters.
|
||||
# {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.
|
||||
MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected.
|
||||
NameSearchFilter.errorMessage.emtpyName=Please input a name to search.
|
||||
@ -63,6 +65,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
|
||||
FileSearchPanel.searchButton.text=Search
|
||||
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
|
||||
HashSearchPanel.md5CheckBox.text=MD5:
|
||||
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
|
||||
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
|
||||
FileSearchPanel.errorLabel.text=\
|
||||
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.dateFormatLabel.text=*The date format is mm/dd/yyyy
|
||||
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
|
||||
public boolean isEnabled() {
|
||||
return this.getComponent().getHashCheckBox().isSelected();
|
||||
return (this.getComponent().getMd5HashCheckBox().isSelected()
|
||||
|| this.getComponent().getSha256HashCheckBox().isSelected());
|
||||
}
|
||||
|
||||
@Override
|
||||
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()) {
|
||||
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
|
||||
if (md5Hash.isEmpty()) {
|
||||
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
|
||||
@ -63,23 +81,45 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
|
||||
@Override
|
||||
@Messages({
|
||||
"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."
|
||||
})
|
||||
public boolean isValid() {
|
||||
String inputHashData = this.getComponent().getSearchTextField().getText();
|
||||
if (inputHashData.isEmpty()) {
|
||||
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
|
||||
return false;
|
||||
if (this.getComponent().getMd5HashCheckBox().isSelected()) {
|
||||
String inputHashData = this.getComponent().getMd5TextField().getText();
|
||||
if (inputHashData.isEmpty()) {
|
||||
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()));
|
||||
return false;
|
||||
}
|
||||
if (!inputHashData.matches("[0-9a-fA-F]+")) {
|
||||
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
|
||||
return false;
|
||||
|
||||
if (this.getComponent().getSha256HashCheckBox().isSelected()) {
|
||||
String inputHashData = this.getComponent().getSha256TextField().getText();
|
||||
if (inputHashData.isEmpty()) {
|
||||
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -56,34 +56,67 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" 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"/>
|
||||
<Component id="searchTextField" min="-2" pref="247" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="hashCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="searchTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="3" 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>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JCheckBox" name="hashCheckBox">
|
||||
<Component class="javax.swing.JCheckBox" name="md5CheckBox">
|
||||
<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.md5CheckBox.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="hashCheckBoxActionPerformed"/>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="md5CheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</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>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -45,19 +45,19 @@ class HashSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private void customizeComponents() {
|
||||
|
||||
searchTextField.setComponentPopupMenu(rightClickMenu);
|
||||
md5TextField.setComponentPopupMenu(rightClickMenu);
|
||||
ActionListener actList = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JMenuItem jmi = (JMenuItem) e.getSource();
|
||||
if (jmi.equals(cutMenuItem)) {
|
||||
searchTextField.cut();
|
||||
md5TextField.cut();
|
||||
} else if (jmi.equals(copyMenuItem)) {
|
||||
searchTextField.copy();
|
||||
md5TextField.copy();
|
||||
} else if (jmi.equals(pasteMenuItem)) {
|
||||
searchTextField.paste();
|
||||
md5TextField.paste();
|
||||
} else if (jmi.equals(selectAllMenuItem)) {
|
||||
searchTextField.selectAll();
|
||||
md5TextField.selectAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -65,7 +65,24 @@ class HashSearchPanel extends javax.swing.JPanel {
|
||||
copyMenuItem.addActionListener(actList);
|
||||
pasteMenuItem.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
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
@ -84,17 +101,27 @@ class HashSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
}
|
||||
|
||||
JCheckBox getHashCheckBox() {
|
||||
return hashCheckBox;
|
||||
JCheckBox getMd5HashCheckBox() {
|
||||
return md5CheckBox;
|
||||
}
|
||||
|
||||
JTextField getSearchTextField() {
|
||||
return searchTextField;
|
||||
JTextField getMd5TextField() {
|
||||
return md5TextField;
|
||||
}
|
||||
|
||||
JCheckBox getSha256HashCheckBox() {
|
||||
return sha256CheckBox;
|
||||
}
|
||||
|
||||
JTextField getSha256TextField() {
|
||||
return sha256TextField;
|
||||
}
|
||||
|
||||
void setComponentsEnabled() {
|
||||
boolean enabled = hashCheckBox.isSelected();
|
||||
this.searchTextField.setEnabled(enabled);
|
||||
boolean md5Enabled = md5CheckBox.isSelected();
|
||||
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();
|
||||
pasteMenuItem = new javax.swing.JMenuItem();
|
||||
selectAllMenuItem = new javax.swing.JMenuItem();
|
||||
hashCheckBox = new javax.swing.JCheckBox();
|
||||
searchTextField = new javax.swing.JTextField();
|
||||
md5CheckBox = new javax.swing.JCheckBox();
|
||||
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
|
||||
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
|
||||
rightClickMenu.add(selectAllMenuItem);
|
||||
|
||||
hashCheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
|
||||
hashCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
md5CheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
|
||||
md5CheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
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);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(hashCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(sha256CheckBox)
|
||||
.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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(hashCheckBox)
|
||||
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.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
|
||||
|
||||
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();
|
||||
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
|
||||
private javax.swing.JMenuItem copyMenuItem;
|
||||
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.JPopupMenu rightClickMenu;
|
||||
private javax.swing.JTextField searchTextField;
|
||||
private javax.swing.JMenuItem selectAllMenuItem;
|
||||
private javax.swing.JCheckBox sha256CheckBox;
|
||||
private javax.swing.JTextField sha256TextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -208,10 +208,17 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
// Safely get a reference to the totalsForIngestJobs object
|
||||
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
|
||||
|
||||
// calc hash value
|
||||
String md5Hash = getHash(file, totals);
|
||||
if (md5Hash == null) {
|
||||
return ProcessResult.ERROR;
|
||||
// calc hash values
|
||||
try {
|
||||
calculateHashes(file, totals);
|
||||
} 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
|
||||
@ -451,50 +458,46 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the md5 hash for a file or generates one if no one exists on
|
||||
* the file.
|
||||
* Generates hashes for the given file if they haven't already been set.
|
||||
* Hashes are saved to the AbstractFile object.
|
||||
*
|
||||
* @param file The file in order to determine the hash.
|
||||
* @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();
|
||||
if (md5Hash != null && md5Hash.isEmpty()) {
|
||||
return md5Hash;
|
||||
String sha256Hash = file.getSha256Hash();
|
||||
if ((md5Hash != null && ! md5Hash.isEmpty())
|
||||
&& (sha256Hash != null && ! sha256Hash.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
||||
long calcstart = System.currentTimeMillis();
|
||||
md5Hash = HashUtility.calculateMd5Hash(file);
|
||||
if (file.getSize() > 0) {
|
||||
// Surprisingly, the hash calculation does not seem to be correlated that
|
||||
// strongly with file size until the files get large.
|
||||
// Only normalize if the file size is greater than ~1MB.
|
||||
if (file.getSize() < 1000000) {
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
} else {
|
||||
// In testing, this normalization gave reasonable resuls
|
||||
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
|
||||
}
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
||||
long calcstart = System.currentTimeMillis();
|
||||
List<HashUtility.HashResult> newHashResults =
|
||||
HashUtility.calculateHashes(file, Arrays.asList(HashUtility.HashType.MD5,HashUtility.HashType.SHA256 ));
|
||||
if (file.getSize() > 0) {
|
||||
// Surprisingly, the hash calculation does not seem to be correlated that
|
||||
// strongly with file size until the files get large.
|
||||
// Only normalize if the file size is greater than ~1MB.
|
||||
if (file.getSize() < 1000000) {
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
} else {
|
||||
// In testing, this normalization gave reasonable resuls
|
||||
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.
|
||||
// ingest the data sources
|
||||
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) {
|
||||
logger.log(Level.INFO, "Interrupted while ingesting virtual machine file " + file + " in folder " + folder, ex); //NON-NLS
|
||||
} 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
|
||||
* ingest context.
|
||||
* If the image was added, start analysis on it with the ingest modules for this
|
||||
* ingest context. Note that this does not wait for ingest to complete.
|
||||
*/
|
||||
if (!dspCallback.vmDataSources.isEmpty()) {
|
||||
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,
|
||||
VMExtractorIngestModuleFactory.getModuleName(),
|
||||
NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.addedVirtualMachineImage.message", vmFile.toString())));
|
||||
IngestManager.getInstance().queueIngestJob(dataSourceContent, ingestJobSettings);
|
||||
IngestManager.getInstance().beginIngestJob(dataSourceContent, ingestJobSettings);
|
||||
} else {
|
||||
Case.getCurrentCaseThrows().notifyFailedAddingDataSource(taskId);
|
||||
}
|
||||
|
@ -1117,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
|
||||
newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
|
||||
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,
|
||||
newParent, trans);
|
||||
} 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();
|
||||
|
||||
// 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) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
@ -1267,7 +1268,8 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getEnabledCorrelationTypes();
|
||||
|
||||
// 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) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
@ -1278,7 +1280,8 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getSupportedCorrelationTypes();
|
||||
|
||||
// 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) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
|
@ -218,7 +218,9 @@ class InterCaseTestUtils {
|
||||
// kitchenSink.add(keywordSearchTemplate);
|
||||
|
||||
this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink);
|
||||
}
|
||||
|
||||
void setupCorrelationTypes() {
|
||||
try {
|
||||
Collection<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
||||
|
||||
@ -254,16 +256,16 @@ class InterCaseTestUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Map<Long, String> getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException {
|
||||
return DataSourceLoader.getAllDataSources();
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Integer> getCaseMap() throws CentralRepoException {
|
||||
|
||||
if (CentralRepository.isEnabled()) {
|
||||
Map<String, Integer> mapOfCaseIdsToCase = new HashMap<>();
|
||||
|
||||
|
||||
for (CorrelationCase correlationCase : CentralRepository.getInstance().getCases()) {
|
||||
mapOfCaseIdsToCase.put(correlationCase.getDisplayName(), correlationCase.getID());
|
||||
}
|
||||
@ -300,7 +302,7 @@ class InterCaseTestUtils {
|
||||
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, crSettings);
|
||||
centralRepoSchemaFactory.initializeDatabaseSchema();
|
||||
centralRepoSchemaFactory.insertDefaultDatabaseContent();
|
||||
|
||||
|
||||
crSettings.saveSettings();
|
||||
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.SQLITE);
|
||||
}
|
||||
@ -313,10 +315,10 @@ class InterCaseTestUtils {
|
||||
* The length of caseNames and caseDataSourcePaths should be the same, and
|
||||
* cases should appear in the same order.
|
||||
*
|
||||
* @param caseNames list case names
|
||||
* @param caseDataSourcePaths two dimensional array listing the datasources
|
||||
* in each case
|
||||
* @param ingestJobSettings HashLookup FileType etc...
|
||||
* @param caseNames list case names
|
||||
* @param caseDataSourcePaths two dimensional array listing the datasources
|
||||
* in each case
|
||||
* @param ingestJobSettings HashLookup FileType etc...
|
||||
* @param caseReferenceToStore
|
||||
*/
|
||||
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);
|
||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||
List<AbstractFile> results = fileManager.findFiles("%%");
|
||||
assertEquals(70, results.size());
|
||||
assertEquals(71, results.size());
|
||||
int carvedJpgGifFiles = 0;
|
||||
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
|
||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
@ -35,6 +36,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.testutils.TskMockUtils;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.sleuthkit.autopsy.testutils.RandomizationUtils;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_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
|
||||
*/
|
||||
public class GetArtifactsTest {
|
||||
public class DataSourceInfoUtilitiesTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
@ -145,38 +147,13 @@ public class GetArtifactsTest {
|
||||
|
||||
List<BlackboardArtifact> toRet = new ArrayList<>();
|
||||
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))));
|
||||
}
|
||||
|
||||
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
|
||||
* 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,
|
||||
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),
|
||||
dataSource, values, attrMaker);
|
||||
|
||||
List<BlackboardArtifact> mixedUpArtifacts = getMixedUp(sortedArtifacts);
|
||||
List<BlackboardArtifact> mixedUpArtifacts = RandomizationUtils.getMixedUp(sortedArtifacts);
|
||||
|
||||
List<BlackboardArtifact> expectedArtifacts = count == 0
|
||||
? sortedArtifacts
|
||||
@ -250,17 +227,17 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortAscending() throws TskCoreException {
|
||||
public void getArtifacts_sortAscending() throws TskCoreException {
|
||||
testAscDesc(SortOrder.ASCENDING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortDescending() throws TskCoreException {
|
||||
public void getArtifacts_sortDescending() throws TskCoreException {
|
||||
testAscDesc(SortOrder.DESCENDING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLimits() throws TskCoreException {
|
||||
public void getArtifacts_limits() throws TskCoreException {
|
||||
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, 5);
|
||||
@ -281,11 +258,11 @@ public class GetArtifactsTest {
|
||||
private <T> void testFailOnBadAttrType(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, T val,
|
||||
AttrMaker<T> attrMaker) throws TskCoreException {
|
||||
|
||||
DataSource dataSource = TskMockUtils.mockDataSource(1);
|
||||
DataSource dataSource = TskMockUtils.getDataSource(1);
|
||||
|
||||
List<BlackboardArtifact> artifacts = Arrays.asList(
|
||||
TskMockUtils.mockArtifact(artifactType, 2, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val)),
|
||||
TskMockUtils.mockArtifact(artifactType, 3, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val))
|
||||
TskMockUtils.getArtifact(artifactType, 2, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val)),
|
||||
TskMockUtils.getArtifact(artifactType, 3, dataSource, attrMaker.make(attributeType, "TEST SOURCE", val))
|
||||
);
|
||||
test(artifactType,
|
||||
dataSource,
|
||||
@ -299,7 +276,7 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnJson() throws TskCoreException {
|
||||
public void getArtifacts_failOnJson() throws TskCoreException {
|
||||
testFailOnBadAttrType(
|
||||
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_GPS_ROUTE),
|
||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS),
|
||||
@ -308,7 +285,7 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnBytes() throws TskCoreException {
|
||||
public void getArtifacts_failOnBytes() throws TskCoreException {
|
||||
testFailOnBadAttrType(
|
||||
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),
|
||||
@ -317,20 +294,20 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPurgeAttrNotPresent() throws TskCoreException {
|
||||
public void getArtifacts_purgeAttrNotPresent() throws TskCoreException {
|
||||
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 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_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));
|
||||
|
||||
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_DATETIME, "TEST SOURCE", 3 * day));
|
||||
|
||||
@ -346,20 +323,20 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultAttrsPresent() throws TskCoreException {
|
||||
public void getArtifacts_multipleAttrsPresent() throws TskCoreException {
|
||||
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 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_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));
|
||||
|
||||
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_DATETIME, "TEST SOURCE", 3 * day));
|
||||
|
||||
@ -375,9 +352,9 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTskCoreExceptionThrown() throws TskCoreException {
|
||||
public void getArtifacts_tskCoreExceptionThrown() throws TskCoreException {
|
||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||
TskMockUtils.mockDataSource(1),
|
||||
TskMockUtils.getDataSource(1),
|
||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||
SortOrder.ASCENDING,
|
||||
0,
|
||||
@ -388,9 +365,9 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowOnLessThan0() throws TskCoreException {
|
||||
public void getArtifacts_throwOnLessThan0() throws TskCoreException {
|
||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||
TskMockUtils.mockDataSource(1),
|
||||
TskMockUtils.getDataSource(1),
|
||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||
SortOrder.ASCENDING,
|
||||
-1,
|
||||
@ -401,9 +378,9 @@ public class GetArtifactsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyListReturned() throws TskCoreException {
|
||||
public void getArtifacts_emptyListReturned() throws TskCoreException {
|
||||
test(new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ACCOUNT),
|
||||
TskMockUtils.mockDataSource(1),
|
||||
TskMockUtils.getDataSource(1),
|
||||
new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE),
|
||||
SortOrder.ASCENDING,
|
||||
0,
|
||||
@ -412,4 +389,76 @@ public class GetArtifactsTest {
|
||||
Collections.emptyList(),
|
||||
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,
|
||||
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,
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.Stream;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
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.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
@ -44,7 +55,7 @@ public class TskMockUtils {
|
||||
*
|
||||
* @return The mocked datasource.
|
||||
*/
|
||||
public static DataSource mockDataSource(long dataSourceId) {
|
||||
public static DataSource getDataSource(long dataSourceId) {
|
||||
DataSource dataSource = mock(DataSource.class);
|
||||
when(dataSource.getName()).thenReturn("");
|
||||
when(dataSource.getId()).thenReturn(dataSourceId);
|
||||
@ -65,14 +76,35 @@ public class TskMockUtils {
|
||||
*
|
||||
* @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 {
|
||||
|
||||
BlackboardArtifact artifact = mock(BlackboardArtifact.class);
|
||||
|
||||
final Map<BlackboardAttribute.Type, BlackboardAttribute> attributeTypes = Stream.of(attributes)
|
||||
.filter(attr -> attr != null)
|
||||
.collect(Collectors.toMap((attr) -> attr.getAttributeType(), Function.identity()));
|
||||
|
||||
when(artifact.getParent()).thenReturn(parent);
|
||||
|
||||
when(artifact.getArtifactID()).thenReturn(artifactId);
|
||||
|
||||
when(artifact.getArtifactTypeID()).thenReturn(artifactType.getTypeID());
|
||||
@ -89,6 +121,146 @@ public class TskMockUtils {
|
||||
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() {
|
||||
}
|
||||
}
|
||||
|
@ -2930,7 +2930,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
||||
String eventType = event.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
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.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
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 Case currentCase;
|
||||
|
||||
|
||||
/**
|
||||
* Empty constructor.
|
||||
*/
|
||||
@ -152,7 +154,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isMbox) {
|
||||
return processMBox(abstractFile);
|
||||
}
|
||||
@ -168,7 +170,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
if (isVcardFile) {
|
||||
return processVcard(abstractFile);
|
||||
}
|
||||
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@ -211,12 +213,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
PstParser parser = new PstParser(services);
|
||||
PstParser.ParseResult result = parser.open(file, abstractFile.getId());
|
||||
|
||||
|
||||
|
||||
switch( result) {
|
||||
case OK:
|
||||
Iterator<EmailMessage> pstMsgIterator = parser.getEmailMessageIterator();
|
||||
if (pstMsgIterator != null) {
|
||||
processEmails(parser.getPartialEmailMessages(), pstMsgIterator , abstractFile);
|
||||
processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile);
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
@ -272,7 +275,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
if (file.delete() == false) {
|
||||
logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@ -451,7 +454,9 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
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())) {
|
||||
derivedFiles.addAll(handleAttachments(message.getAttachments(), abstractFile, msgArtifact));
|
||||
@ -527,7 +532,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
* @param fullMessageIterator
|
||||
* @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
|
||||
// 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())) {
|
||||
derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact ));
|
||||
@ -676,7 +685,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
* @return The generated e-mail message artifact.
|
||||
*/
|
||||
@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;
|
||||
List<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
String to = email.getRecipients();
|
||||
@ -706,7 +715,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
if (senderAddressList.size() == 1) {
|
||||
senderAddress = senderAddressList.get(0);
|
||||
try {
|
||||
senderAccountInstance = currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
senderAccountInstance = accountFileInstanceCache.getAccountInstance(senderAddress);
|
||||
}
|
||||
catch(TskCoreException ex) {
|
||||
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;
|
||||
}
|
||||
try {
|
||||
AccountFileInstance recipientAccountInstance =
|
||||
currentCase.getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
|
||||
EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
AccountFileInstance recipientAccountInstance = accountFileInstanceCache.getAccountInstance(addr);
|
||||
recipientAccountInstances.add(recipientAccountInstance);
|
||||
}
|
||||
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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user