From 44ad30fd8085d29221513da9b21cf85da56d09a0 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 9 Aug 2021 15:36:33 -0400 Subject: [PATCH] Refactored PastCasesSummary --- .../PastCasesSummary.java | 84 ++++++------------- .../datamodel/PastCasesSummaryGetter.java | 76 +++++++++++++++++ .../datasourcesummary/ui/PastCasesPanel.java | 26 ++---- .../Bundle.properties-MERGED | 5 ++ .../ExcelExportAction.java | 9 ++ .../ExportPastCases.java | 76 +++++++++++++++++ .../datasourcesummaryexport/ExportTypes.java | 2 +- 7 files changed, 197 insertions(+), 81 deletions(-) rename Core/src/org/sleuthkit/autopsy/{datasourcesummary/datamodel => contentutils}/PastCasesSummary.java (78%) mode change 100644 => 100755 create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummaryGetter.java create mode 100755 Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java b/Core/src/org/sleuthkit/autopsy/contentutils/PastCasesSummary.java old mode 100644 new mode 100755 similarity index 78% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java rename to Core/src/org/sleuthkit/autopsy/contentutils/PastCasesSummary.java index e7776fbf62..d50f2b44d5 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/contentutils/PastCasesSummary.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; +package org.sleuthkit.autopsy.contentutils; import java.util.ArrayList; import java.util.Arrays; @@ -25,14 +25,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; -import org.sleuthkit.autopsy.contentutils.DataSourceInfoUtilities; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -63,7 +61,7 @@ import org.sleuthkit.datamodel.TskCoreException; * d) The content of that TSK_COMMENT attribute will be of the form "Previous * Case: case1,case2...caseN" */ -public class PastCasesSummary implements DefaultArtifactUpdateGovernor { +public class PastCasesSummary { /** * Return type for results items in the past cases tab. @@ -99,11 +97,6 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { } } - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), - ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() - )); - private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim(); private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT); private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); @@ -118,39 +111,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { private static final String CASE_SEPARATOR = ","; private static final String PREFIX_END = ":"; - private final SleuthkitCaseProvider caseProvider; - private final java.util.logging.Logger logger; - - /** - * Main constructor. - */ - public PastCasesSummary() { - this( - SleuthkitCaseProvider.DEFAULT, - org.sleuthkit.autopsy.coreutils.Logger.getLogger(PastCasesSummary.class.getName()) - ); - - } - - /** - * Main constructor with external dependencies specified. This constructor - * is designed with unit testing in mind since mocked dependencies can be - * utilized. - * - * @param provider The object providing the current SleuthkitCase. - * @param logger The logger to use. - */ - public PastCasesSummary( - SleuthkitCaseProvider provider, - java.util.logging.Logger logger) { - - this.caseProvider = provider; - this.logger = logger; - } - - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; + private PastCasesSummary() { } /** @@ -225,7 +186,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * @return The list of unique cases and their occurrences sorted from max to * min. */ - private List> getCaseCounts(Stream cases) { + private static List> getCaseCounts(Stream cases) { Collection> groupedCases = cases // group by case insensitive compare of cases .collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim())) @@ -250,21 +211,24 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * * @return The artifact if found or null if not. * - * @throws SleuthkitCaseProviderException + * @throws NoCurrentCaseException */ - private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException { + private static BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws NoCurrentCaseException { Long parentId = DataSourceInfoUtilities.getLongOrNull(artifact, TYPE_ASSOCIATED_ARTIFACT); if (parentId == null) { return null; } - SleuthkitCase skCase = caseProvider.get(); try { - return skCase.getArtifactByArtifactId(parentId); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, - String.format("There was an error fetching the parent artifact of a TSK_INTERESTING_ARTIFACT_HIT (parent id: %d)", parentId), - ex); + return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactByArtifactId(parentId); + } catch (TskCoreException ignore) { + /* + * I'm not certain why we ignore this, but it was previously simply + * logged as warning so I'm keeping the original logic + * logger.log(Level.WARNING, String.format("There was an error + * fetching the parent artifact of a TSK_INTERESTING_ARTIFACT_HIT + * (parent id: %d)", parentId), ex); + */ return null; } } @@ -276,9 +240,9 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * * @return True if there is a device associated artifact. * - * @throws SleuthkitCaseProviderException + * @throws NoCurrentCaseException */ - private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException { + private static boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws NoCurrentCaseException { BlackboardArtifact parent = getParentArtifact(artifact); if (parent == null) { return false; @@ -294,17 +258,17 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * * @return The retrieved data or null if null dataSource. * - * @throws SleuthkitCaseProviderException + * @throws NoCurrentCaseException * @throws TskCoreException */ - public PastCasesResult getPastCasesData(DataSource dataSource) - throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + public static PastCasesResult getPastCasesData(DataSource dataSource) + throws NoCurrentCaseException, TskCoreException { if (dataSource == null) { return null; } - SleuthkitCase skCase = caseProvider.get(); + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); List deviceArtifactCases = new ArrayList<>(); List nonDeviceArtifactCases = new ArrayList<>(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummaryGetter.java new file mode 100644 index 0000000000..4d33ad6757 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummaryGetter.java @@ -0,0 +1,76 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.datasourcesummary.datamodel; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import org.sleuthkit.autopsy.contentutils.PastCasesSummary; +import org.sleuthkit.autopsy.contentutils.PastCasesSummary.PastCasesResult; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Wrapper class for converting org.sleuthkit.autopsy.contentutils.PastCasesSummary + * functionality into a DefaultArtifactUpdateGovernor used by PastCases tab. + */ +public class PastCasesSummaryGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + )); + + public PastCasesSummaryGetter() { + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * Returns the past cases data to be shown in the past cases tab. + * + * @param dataSource The data source. + * + * @return The retrieved data or null if null dataSource. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public PastCasesResult getPastCasesData(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + + if (dataSource == null) { + return null; + } + + try { + return PastCasesSummary.getPastCasesData(dataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java index cdce933a1c..5886457530 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java @@ -22,8 +22,8 @@ import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; +import org.sleuthkit.autopsy.contentutils.PastCasesSummary.PastCasesResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummaryGetter; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; @@ -80,19 +80,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel { private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final DataFetcher pastCasesFetcher; - + public PastCasesPanel() { - this(new PastCasesSummary()); + this(new PastCasesSummaryGetter()); } /** * Creates new form PastCasesPanel */ - public PastCasesPanel(PastCasesSummary pastCaseData) { + public PastCasesPanel(PastCasesSummaryGetter pastCaseData) { super(pastCaseData); this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource); - + // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchWorker.DataFetchComponents<>( @@ -124,20 +124,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel { onNewDataSource(dataFetchComponents, tables, dataSource); } - /* ELTODO - @Override - List getExports(DataSource dataSource) { - PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource); - if (result == null) { - return Collections.emptyList(); - } - - return Arrays.asList( - getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_notableFileTable_tabName(), result.getTaggedNotable()), - getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_sameIdsTable_tabName(), result.getSameIdsResults()) - ); - }*/ - @Override public void close() { ingestRunningLabel.unregister(); diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED index e66edab769..a0c9bb1d6e 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED @@ -33,6 +33,11 @@ ExportContainerInfo_export_timeZone=Time Zone: ExportContainerInfo_export_unallocatedSize=Unallocated Space: ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A ExportContainerInfo_tabName=Container +ExportPastCases_caseColumn_title=Case +ExportPastCases_countColumn_title=Count +ExportPastCases_notableFileTable_tabName=Cases with Common Notable +ExportPastCases_onNoCrIngest_message=No results will be shown because the Central Repository module was not run. +ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices ExportRecentFiles_attachmentsTable_tabName=Recent Attachments ExportRecentFiles_col_head_date=Date ExportRecentFiles_col_header_domain=Domain diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java index 4524104515..50dd05a18c 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java @@ -97,6 +97,7 @@ class ExcelExportAction { "ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data", "ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data", "ExcelExportAction_exportToXLSX_gatheringAnalysisData=Fetching Analysis Data", + "ExcelExportAction_exportToXLSX_gatheringPastData=Fetching Historical Data", "ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",}) void exportToXLSX(ReportProgressPanel progressPanel, DataSource dataSource, String baseReportDir) @@ -148,6 +149,14 @@ class ExcelExportAction { if (exports != null) { sheetExports.addAll(exports); } + + // Export hash set hits, keyword hits, and interesting item hits + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringPastData()); + progressPanel.setProgress(6); + exports = ExportPastCases.getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_writingToFile()); progressPanel.setProgress(9); diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java new file mode 100755 index 0000000000..6448adb369 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java @@ -0,0 +1,76 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 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.report.modules.datasourcesummaryexport; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.contentutils.PastCasesSummary; +import org.sleuthkit.autopsy.contentutils.PastCasesSummary.PastCasesResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getFetchResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getTableExport; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export information about a datasource and how it pertains to other + * cases. + */ +@Messages({ + "ExportPastCases_caseColumn_title=Case", + "ExportPastCases_countColumn_title=Count", + "ExportPastCases_notableFileTable_tabName=Cases with Common Notable", + "ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices",}) +class ExportPastCases { + + // model for column indicating the case + private static final ColumnModel, DefaultCellModel> CASE_COL = new ColumnModel<>( + Bundle.ExportPastCases_caseColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getKey()), + 300 + ); + + // model for column indicating the count + private static final ColumnModel, DefaultCellModel> COUNT_COL = new ColumnModel<>( + Bundle.ExportPastCases_countColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getValue()), + 100 + ); + + // the template for columns in both tables in this tab + private static List, DefaultCellModel>> DEFAULT_TEMPLATE + = Arrays.asList(CASE_COL, COUNT_COL); + + private ExportPastCases() { + } + + static List getExports(DataSource dataSource) { + DataFetcher pastCasesFetcher = (ds) -> PastCasesSummary.getPastCasesData(ds); + PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource); + if (result == null) { + return Collections.emptyList(); + } + + return Arrays.asList( + getTableExport(DEFAULT_TEMPLATE, Bundle.ExportPastCases_notableFileTable_tabName(), result.getTaggedNotable()), + getTableExport(DEFAULT_TEMPLATE, Bundle.ExportPastCases_sameIdsTable_tabName(), result.getSameIdsResults()) + ); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java index 5453a879c8..9253b5b09b 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java @@ -40,7 +40,7 @@ import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskCoreException; /** - * Panel for displaying summary information on the known files present in the + * Class to export summary information on the known files present in the * specified DataSource. */ @Messages({