diff --git a/Core/build.xml b/Core/build.xml index 173dda76a5..98644b8cb3 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -96,6 +96,10 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java index f752fbabad..044241ccf5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java @@ -24,7 +24,7 @@ import java.util.Map; /** * Provides logic for selecting common files from all data sources. */ -final class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder { +final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder { private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL)%s GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS @@ -36,7 +36,7 @@ final class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilde * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types */ - AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java index 7b7b10b828..de06db45a4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java @@ -26,7 +26,7 @@ import java.util.Map; * Utility and wrapper model around data required for Common Files Search results. * Subclass this to implement different selections of files from the case. */ -final class CommonFilesMetadata { +final public class CommonFilesMetadata { private final Map metadata; @@ -52,7 +52,7 @@ final class CommonFilesMetadata { return this.metadata.get(md5); } - Map getMetadata() { + public Map getMetadata() { return Collections.unmodifiableMap(this.metadata); } @@ -60,7 +60,7 @@ final class CommonFilesMetadata { * How many distinct file instances exist for this metadata? * @return number of file instances */ - int size() { + public int size() { int count = 0; for (Md5Metadata data : this.metadata.values()) { count += data.size(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java index d137368cfe..6fee774102 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java @@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException; * This entire thing runs on a background thread where exceptions are handled. */ @SuppressWarnings("PMD.AbstractNaming") -abstract class CommonFilesMetadataBuilder { +public abstract class CommonFilesMetadataBuilder { private final Map dataSourceIdToNameMap; private final boolean filterByMedia; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index 630efaead3..3ce2c34e26 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -18,12 +18,9 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.io.File; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; @@ -33,7 +30,6 @@ import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.openide.explorer.ExplorerManager; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; @@ -42,8 +38,6 @@ import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; /** @@ -76,7 +70,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { initComponents(); this.setupDataSources(); - + this.errorText.setVisible(false); } @@ -97,10 +91,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel { new SwingWorker, Void>() { - private static final String SELECT_DATA_SOURCES_LOGICAL = "select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; - - private static final String SELECT_DATA_SOURCES_IMAGE = "select obj_id, name from tsk_image_names where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; - private void updateUi() { String[] dataSourcesNames = new String[CommonFilesPanel.this.dataSourceMap.size()]; @@ -131,48 +121,10 @@ public final class CommonFilesPanel extends javax.swing.JPanel { return CommonFilesPanel.this.dataSourceMap.size() >= 2; } - private void loadLogicalSources(SleuthkitCase tskDb, Map dataSouceMap) throws TskCoreException, SQLException { - //try block releases resources - exceptions are handled in done() - try ( - CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); - ResultSet resultSet = query.getResultSet()) { - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - dataSouceMap.put(objectId, dataSourceName); - } - } - } - - private void loadImageSources(SleuthkitCase tskDb, Map dataSouceMap) throws SQLException, TskCoreException { - //try block releases resources - exceptions are handled in done() - try ( - CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); - ResultSet resultSet = query.getResultSet()) { - - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - File image = new File(dataSourceName); - String dataSourceNameTrimmed = image.getName(); - dataSouceMap.put(objectId, dataSourceNameTrimmed); - } - } - } - @Override protected Map doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException { - - Map dataSouceMap = new HashMap<>(); - - Case currentCase = Case.getCurrentCaseThrows(); - SleuthkitCase tskDb = currentCase.getSleuthkitCase(); - - loadLogicalSources(tskDb, dataSouceMap); - - loadImageSources(tskDb, dataSouceMap); - - return dataSouceMap; + DataSourceLoader loader = new DataSourceLoader(); + return loader.getDataSourceMap(); } @Override @@ -295,10 +247,10 @@ public final class CommonFilesPanel extends javax.swing.JPanel { TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode); DataResultViewerTable table = new DataResultViewerTable(); - + Collection viewers = new ArrayList<>(1); viewers.add(table); - + DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); } catch (InterruptedException ex) { @@ -590,7 +542,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { this.pictureVideoCheckbox.setEnabled(true); this.documentsCheckbox.setEnabled(true); - + this.toggleErrorTextAndSearchBox(); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java new file mode 100644 index 0000000000..69af771dad --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java @@ -0,0 +1,95 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.io.File; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Encapsulates logic required to create a mapping of data sources in the + * current case to their data source IDs. + * + * Intended to be used within the context of a SwingWorker or other background + * thread. + */ +public class DataSourceLoader { + + private static final String SELECT_DATA_SOURCES_LOGICAL = "select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; + + private static final String SELECT_DATA_SOURCES_IMAGE = "select obj_id, name from tsk_image_names where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; + + private void loadLogicalSources(SleuthkitCase tskDb, Map dataSouceMap) throws TskCoreException, SQLException { + //try block releases resources - exceptions are handled in done() + try ( + SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); + ResultSet resultSet = query.getResultSet() + ) { + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String dataSourceName = resultSet.getString(2); + dataSouceMap.put(objectId, dataSourceName); + } + } + } + + private void loadImageSources(SleuthkitCase tskDb, Map dataSouceMap) throws SQLException, TskCoreException { + //try block releases resources - exceptions are handled in done() + try ( + SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); + ResultSet resultSet = query.getResultSet()) { + + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String dataSourceName = resultSet.getString(2); + File image = new File(dataSourceName); + String dataSourceNameTrimmed = image.getName(); + dataSouceMap.put(objectId, dataSourceNameTrimmed); + } + } + } + + /** + * Get a map of data source Ids to their string names for the current case. + * + * @return Map of Long (id) to String (name) + * @throws NoCurrentCaseException + * @throws TskCoreException + * @throws SQLException + */ + public Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException { + Map dataSouceMap = new HashMap<>(); + + Case currentCase = Case.getCurrentCaseThrows(); + SleuthkitCase tskDb = currentCase.getSleuthkitCase(); + + loadLogicalSources(tskDb, dataSouceMap); + + loadImageSources(tskDb, dataSouceMap); + + return dataSouceMap; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java index bc506f5988..f3f8ef689d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java @@ -24,7 +24,7 @@ import java.util.Map; /** * Provides logic for selecting common files from a single data source. */ -final class SingleDataSource extends CommonFilesMetadataBuilder { +final public class SingleDataSource extends CommonFilesMetadataBuilder { private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS private final Long selectedDataSourceId; @@ -35,10 +35,12 @@ final class SingleDataSource extends CommonFilesMetadataBuilder { * once in the given data source * @param dataSourceId data source id for which common files must appear at least once * @param dataSourceIdMap a map of obj_id to datasource name - * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types - * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types + * @param filterByMediaMimeType match only on files whose mime types can be + * broadly categorized as media types + * @param filterByDocMimeType match only on files whose mime types can be + * broadly categorized as document types */ - SingleDataSource(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public SingleDataSource(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); this.selectedDataSourceId = dataSourceId; this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeyValueNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeyValueNode.java index 2904a1aecb..fe063ebf9c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeyValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeyValueNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,9 @@ import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; @@ -107,17 +109,21 @@ public class KeyValueNode extends AbstractNode { * * @return actions */ + @Messages({ + "KeyValueNode.menuItemText.viewFileInDir=View Source File in Directory" + }) @Override public Action[] getActions(boolean popup) { - List actions = new ArrayList<>(); - actions.addAll(Arrays.asList(super.getActions(popup))); - //if this artifact has associated content, add the action to view the content in the timeline + List actionsList = new ArrayList<>(); + actionsList.addAll(Arrays.asList(super.getActions(popup))); + // If this artifact has associated content, add the actions. AbstractFile file = getLookup().lookup(AbstractFile.class); if (null != file) { - actions.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); + actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); + actionsList.add(new ViewContextAction(Bundle.KeyValueNode_menuItemText_viewFileInDir(), file)); } - actions.add(null); // creates a menu separator + actionsList.add(null); // creates a menu separator - return actions.toArray(new Action[actions.size()]); + return actionsList.toArray(new Action[actionsList.size()]); } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java index beb3a05277..55498625ef 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java @@ -65,10 +65,12 @@ public class DataSourcePanel extends javax.swing.JPanel { @Override public void mouseMoved(MouseEvent evt) { - JList DsList = (JList) evt.getSource(); - int index = DsList.locationToIndex(evt.getPoint()); - if (index > -1) { - DsList.setToolTipText(toolTipList.get(index)); + if (evt.getSource() instanceof JList) { + JList dsList = (JList) evt.getSource(); + int index = dsList.locationToIndex(evt.getPoint()); + if (index > -1) { + dsList.setToolTipText(toolTipList.get(index)); + } } } }); diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java index d28ef1b475..8facd6cd3e 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java @@ -190,7 +190,7 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { stopTimer(); healthMonitorOutputTimer = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("health_monitor_timer").build()); - healthMonitorOutputTimer.scheduleWithFixedDelay(new PeriodicHealthMonitorTask(), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES); + healthMonitorOutputTimer.scheduleWithFixedDelay(new PeriodicHealthMonitorTask(false), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES); } /** @@ -356,11 +356,16 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { /** * Collect metrics at a scheduled time. + * @param caseIsClosing True if this was triggered from a case closed event * @throws HealthMonitorException */ - private void gatherTimerBasedMetrics() throws HealthMonitorException { - // Time a database query - performDatabaseQuery(); + private void gatherTimerBasedMetrics(boolean caseIsClosing) throws HealthMonitorException { + // Time a database query. If this was triggered from a case close event + // it will fail - since we're on a new thread the case database will + // be in the process of closing. In that case, skip collecting the metric. + if( ! caseIsClosing) { + performDatabaseQuery(); + } } /** @@ -806,17 +811,23 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { */ static final class PeriodicHealthMonitorTask implements Runnable { + boolean caseIsClosing; + + PeriodicHealthMonitorTask(boolean caseIsClosing) { + this.caseIsClosing = caseIsClosing; + } + /** * Perform all periodic tasks: * - Check if monitoring has been enabled / disabled in the database - * - Gather any additional metrics + * - Calculate any final metrics * - Write current metric data to the database */ @Override public void run() { try { getInstance().updateFromGlobalEnabledStatus(); - getInstance().gatherTimerBasedMetrics(); + getInstance().gatherTimerBasedMetrics(caseIsClosing); getInstance().writeCurrentStateToDatabase(); } catch (HealthMonitorException ex) { logger.log(Level.SEVERE, "Error performing periodic task", ex); //NON-NLS @@ -832,7 +843,7 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { case CURRENT_CASE: if ((null == evt.getNewValue()) && (evt.getOldValue() instanceof Case)) { // When a case is closed, write the current metrics to the database - healthMonitorExecutor.submit(new EnterpriseHealthMonitor.PeriodicHealthMonitorTask()); + healthMonitorExecutor.submit(new EnterpriseHealthMonitor.PeriodicHealthMonitorTask(true)); } break; } diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java index 6d995ed9ac..8d4a57b4f6 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java @@ -373,7 +373,7 @@ class TimingMetricGraphPanel extends JPanel { } } else if (y0value > maxValueOnYAxis) { try { - y0value = minValueOnYAxis; + y0value = maxValueOnYAxis; x0value = trendLine.getXGivenY(y0value); } catch (HealthMonitorException ex) { // The exception is caused by a slope of zero on the trend line, which diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties index c80ccaa863..dfda2e6061 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties @@ -9,8 +9,8 @@ OpenIDE-Module-Name=Embedded File Extraction OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin\: {0} EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream. -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=File-level Encryption -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File) +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption (Archive File) EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir\: {0}\: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive\: {0}, item\: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping item in {1}. diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java index 257f90ad2f..068c52cf5e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.modules.hashdatabase; -import java.util.ArrayList; -import java.util.List; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java new file mode 100644 index 0000000000..a599763346 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java @@ -0,0 +1,465 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.*; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm. + */ +public class IngestedWithHashAndFileType extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileType.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public IngestedWithHashAndFileType(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "IngestedWithHashAndFileTypeTests"); + } + + @Override + public void setUp() { + this.utils.setUp(); + + IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); + + ArrayList templates = new ArrayList<>(); + templates.add(hashLookupTemplate); + templates.add(mimeTypeLookupTemplate); + + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestType.FILES_ONLY, templates); + + try { + IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown() { + this.utils.tearDown(); + } + + /** + * Find all matches & all file types. Confirm file.jpg is found on all three + * and file.docx is found on two. + */ + public void testOneA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, true); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & only media types. Confirm same results. + * + */ + public void testTwoB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, true, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, true); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 2 & all file types: Confirm file.jpg. + * + */ + public void testThree() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long second = getDataSourceIdByName(SET2, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(second, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 4 & all file types: Confirm nothing is found. + */ + public void testFour() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long last = getDataSourceIdByName(SET4, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(last, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 3 & all file types: Confirm file.jpg and file.docx. + */ + public void testFive() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long third = getDataSourceIdByName(SET3, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java new file mode 100644 index 0000000000..0090d0f699 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java @@ -0,0 +1,140 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import static junit.framework.Assert.assertTrue; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import static org.sleuthkit.autopsy.testutils.IngestUtils.getIngestModuleTemplate; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Ingested w/o mime type info added to DB. + * + * Setup: + * + * Add images set 1, set 2, set 3, and set 4 to case. Do not run mime type + * module. + */ +public class IngestedWithNoFileTypes extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypes.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public IngestedWithNoFileTypes(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "IngestedWithNoFileTypes"); + } + + @Override + public void setUp() { + this.utils.setUp(); + + IngestModuleTemplate hashLookupTemplate = getIngestModuleTemplate(new HashLookupModuleFactory()); + + ArrayList templates = new ArrayList<>(); + templates.add(hashLookupTemplate); + + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); + + try { + IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown(){ + this.utils.tearDown(); + } + + /** + * Search using all data sources and filtering for media types. We should + * find nothing and no errors should arise. + */ + public void testOne() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(files.isEmpty()); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + } + } + + /** + * Search using single data source and filtering for doc types. Observe that + * nothing is found and that nothing blows up. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long third = IntraCaseUtils.getDataSourceIdByName(IntraCaseUtils.SET3, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, true, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(files.isEmpty()); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java new file mode 100644 index 0000000000..66a68f3a38 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java @@ -0,0 +1,220 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; +import org.sleuthkit.autopsy.commonfilesearch.FileInstanceMetadata; +import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; +import org.sleuthkit.autopsy.testutils.CaseUtils; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * Provides setup and utility for testing presence of files in different data + * sets discoverable by Common Files Features. + * + * Data set definitions: + * + * set 1 + * + file1 + * - IMG_6175.jpg + * + file2 + * - IMG_6175.jpg + * + file3 + * - BasicStyleGuide.doc + * + * set 2 + * - adsf.pdf + * - IMG_6175.jpg + * + * set 3 + * - BasicStyleGuide.doc + * - IMG_6175.jpg + * + * set 4 + * - file.dat (empty file) + */ +class IntraCaseUtils { + + private static final String CASE_NAME = "IntraCaseCommonFilesSearchTest"; + static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), CASE_NAME); + + private final Path imagePath1; + private final Path imagePath2; + private final Path imagePath3; + private final Path imagePath4; + + static final String IMG = "IMG_6175.jpg"; + static final String DOC = "BasicStyleGuide.doc"; + static final String PDF = "adsf.pdf"; //not a typo - it appears this way in the test image + static final String EMPTY = "file.dat"; + + static final String SET1 = "commonfiles_image1_v1.vhd"; + static final String SET2 = "commonfiles_image2_v1.vhd"; + static final String SET3 = "commonfiles_image3_v1.vhd"; + static final String SET4 = "commonfiles_image4_v1.vhd"; + + private final DataSourceLoader dataSourceLoader; + + private final String caseName; + + IntraCaseUtils(NbTestCase nbTestCase, String caseName){ + imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image1_v1.vhd"); + imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image2_v1.vhd"); + imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image3_v1.vhd"); + imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image4_v1.vhd"); + + this.dataSourceLoader = new DataSourceLoader(); + + this.caseName = caseName; + } + + void setUp(){ + CaseUtils.createAsCurrentCase(this.caseName); + + final ImageDSProcessor imageDSProcessor = new ImageDSProcessor(); + + IngestUtils.addDataSource(imageDSProcessor, imagePath1); + IngestUtils.addDataSource(imageDSProcessor, imagePath2); + IngestUtils.addDataSource(imageDSProcessor, imagePath3); + IngestUtils.addDataSource(imageDSProcessor, imagePath4); + } + + Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException{ + return this.dataSourceLoader.getDataSourceMap(); + } + + void tearDown(){ + CaseUtils.closeCurrentCase(false); + try { + CaseUtils.deleteCaseDir(CASE_DIRECTORY_PATH.toFile()); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + //does not represent a failure in the common files search feature + } + } + + /** + * Verify that the given file appears a precise number times in the given + * data source. + * + * @param files search domain + * @param objectIdToDataSource mapping of file ids to data source names + * @param name name of file to search for + * @param dataSource name of data source where file should appear + * @param count number of appearances of the given file + * @return true if a file with the given name exists the specified number + * of times in the given data source + */ + static boolean verifyFileExistanceAndCount(List files, Map objectIdToDataSource, String name, String dataSource, int count) { + + int tally = 0; + + for (AbstractFile file : files) { + + Long objectId = file.getId(); + + String fileName = file.getName(); + + String dataSourceName = objectIdToDataSource.get(objectId); + + if (fileName.equals(name) && dataSourceName.equals(dataSource)) { + tally++; + } + } + + return tally == count; + } + + /** + * Convenience method which verifies that a file exists within a given data + * source exactly once. + * + * @param files search domain + * @param objectIdToDataSource mapping of file ids to data source names + * @param name name of file to search for + * @param dataSource name of data source where file should appear + * @return true if a file with the given name exists once in the given data + * source + */ + static boolean verifySingularFileExistance(List files, Map objectIdToDataSource, String name, String dataSource) { + return verifyFileExistanceAndCount(files, objectIdToDataSource, name, dataSource, 1); + } + + static Map mapFileInstancesToDataSources(CommonFilesMetadata metadata) { + Map instanceIdToDataSource = new HashMap<>(); + + for (Map.Entry entry : metadata.getMetadata().entrySet()) { + for (FileInstanceMetadata md : entry.getValue().getMetadata()) { + instanceIdToDataSource.put(md.getObjectId(), md.getDataSourceName()); + } + } + + return instanceIdToDataSource; + } + + static List getFiles(Set objectIds) { + List files = new ArrayList<>(objectIds.size()); + + for (Long id : objectIds) { + try { + AbstractFile file = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(id); + files.add(file); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + return files; + } + + static Long getDataSourceIdByName(String name, Map dataSources){ + + if(dataSources.containsValue(name)){ + for(Map.Entry dataSource : dataSources.entrySet()){ + if(dataSource.getValue().equals(name)){ + return dataSource.getKey(); + } + } + } else { + throw new IndexOutOfBoundsException(String.format("Name should be one of: {0}", String.join(",", dataSources.values()))); + } + return null; + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java new file mode 100644 index 0000000000..9bd7a02bae --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java @@ -0,0 +1,114 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.sql.SQLException; +import java.util.Map; +import static junit.framework.Assert.assertEquals; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.SET1; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.getDataSourceIdByName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Test that cases which are created but have not run any ingest modules turn up + * no results. + * + * Setup: + * + * Add images set 1, set 2, set 3, and set 4 to case. Do not ingest. + * + */ +public class UningestedCases extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(UningestedCases.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public UningestedCases(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "UningestedCasesTests"); + } + + @Override + public void setUp(){ + this.utils.setUp(); + } + + @Override + public void tearDown(){ + this.utils.tearDown(); + } + + /** + * Find all matches & all file types. Confirm no matches are found (since + * there are no hashes to match). + */ + public void testOne() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + int resultCount = metadata.size(); + assertEquals(resultCount, 0); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches on image #1 & all file types. Confirm no matches. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + int resultCount = metadata.size(); + assertEquals(resultCount, 0); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java index a1bbd0ae24..224f2c9dd6 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java @@ -125,5 +125,4 @@ public final class CaseUtils { */ private CaseUtils() { } - } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java index 15913dfba7..2746e48056 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java @@ -41,7 +41,7 @@ import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer; * Each job with the specified status will have a child node representing it. */ final class AutoIngestJobsNode extends AbstractNode { - + //Event bus is non static so that each instance of this will only listen to events sent to that instance private final EventBus refreshChildrenEventBus; @@ -59,9 +59,14 @@ final class AutoIngestJobsNode extends AbstractNode { /** * Construct a new AutoIngestJobsNode. + * + * @param snapshot the snapshot which contains the AutoIngestJobs + * @param status the status of the jobs being displayed + * @param eventBus the event bus which will be used to send and receive + * refresh events */ - AutoIngestJobsNode(AutoIngestJobStatus status, EventBus eventBus) { - super(Children.create(new AutoIngestNodeChildren(status, eventBus), false)); + AutoIngestJobsNode(JobsSnapshot jobsSnapshot, AutoIngestJobStatus status, EventBus eventBus) { + super(Children.create(new AutoIngestNodeChildren(jobsSnapshot, status, eventBus), false)); refreshChildrenEventBus = eventBus; } @@ -88,9 +93,11 @@ final class AutoIngestJobsNode extends AbstractNode { * * @param snapshot the snapshot which contains the AutoIngestJobs * @param status the status of the jobs being displayed + * @param eventBus the event bus which the class registers to for + * refresh events */ - AutoIngestNodeChildren(AutoIngestJobStatus status, EventBus eventBus) { - jobsSnapshot = new JobsSnapshot(); + AutoIngestNodeChildren(JobsSnapshot snapshot, AutoIngestJobStatus status, EventBus eventBus) { + jobsSnapshot = snapshot; autoIngestJobStatus = status; refreshEventBus = eventBus; refreshChildrenSubscriber.register(refreshEventBus); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java index 7818dd8d68..b9d356f6e8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java @@ -171,7 +171,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa ((AutoIngestJobsNode) explorerManager.getRootContext()).refresh(refreshEvent); } else { //Make a new AutoIngestJobsNode with it's own EventBus and set it as the root context - explorerManager.setRootContext(new AutoIngestJobsNode(status, new EventBus("AutoIngestJobsNodeEventBus"))); + explorerManager.setRootContext(new AutoIngestJobsNode(refreshEvent.getJobsSnapshot(), status, new EventBus("AutoIngestJobsNodeEventBus"))); } outline.setRowSelectionAllowed(true); outline.setFocusable(true); diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index b1adb5d40b..9a9a904cc5 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Mon, 19 Mar 2018 11:17:11 -0700 +#Tue, 08 May 2018 10:29:55 -0600 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 @@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18 SplashRunningTextColor=0x0 SplashRunningTextFontSize=19 -currentVersion=Autopsy 4.6.0 +currentVersion=Autopsy 4.7.0 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 6cb9d4bdea..db3cd01af5 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Fri, 09 Mar 2018 13:03:41 -0700 -CTL_MainWindow_Title=Autopsy 4.6.0 -CTL_MainWindow_Title_No_Project=Autopsy 4.6.0 +#Tue, 08 May 2018 10:29:55 -0600 +CTL_MainWindow_Title=Autopsy 4.7.0 +CTL_MainWindow_Title_No_Project=Autopsy 4.7.0