Refactored PastCasesSummary

This commit is contained in:
Eugene Livis 2021-08-09 15:36:33 -04:00
parent ee2f1c39c3
commit 44ad30fd80
7 changed files with 197 additions and 81 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.contentutils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -25,14 +25,12 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair; 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.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;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; 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 * d) The content of that TSK_COMMENT attribute will be of the form "Previous
* Case: case1,case2...caseN" * Case: case1,case2...caseN"
*/ */
public class PastCasesSummary implements DefaultArtifactUpdateGovernor { public class PastCasesSummary {
/** /**
* Return type for results items in the past cases tab. * Return type for results items in the past cases tab.
@ -99,11 +97,6 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
} }
} }
private static final Set<Integer> 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 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_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT);
private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); 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 CASE_SEPARATOR = ",";
private static final String PREFIX_END = ":"; private static final String PREFIX_END = ":";
private final SleuthkitCaseProvider caseProvider; private PastCasesSummary() {
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<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
} }
/** /**
@ -225,7 +186,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @return The list of unique cases and their occurrences sorted from max to * @return The list of unique cases and their occurrences sorted from max to
* min. * min.
*/ */
private List<Pair<String, Long>> getCaseCounts(Stream<String> cases) { private static List<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
Collection<List<String>> groupedCases = cases Collection<List<String>> groupedCases = cases
// group by case insensitive compare of cases // group by case insensitive compare of cases
.collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim())) .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. * @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); Long parentId = DataSourceInfoUtilities.getLongOrNull(artifact, TYPE_ASSOCIATED_ARTIFACT);
if (parentId == null) { if (parentId == null) {
return null; return null;
} }
SleuthkitCase skCase = caseProvider.get();
try { try {
return skCase.getArtifactByArtifactId(parentId); return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactByArtifactId(parentId);
} catch (TskCoreException ex) { } catch (TskCoreException ignore) {
logger.log(Level.WARNING, /*
String.format("There was an error fetching the parent artifact of a TSK_INTERESTING_ARTIFACT_HIT (parent id: %d)", parentId), * I'm not certain why we ignore this, but it was previously simply
ex); * 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; return null;
} }
} }
@ -276,9 +240,9 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* *
* @return True if there is a device associated artifact. * @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); BlackboardArtifact parent = getParentArtifact(artifact);
if (parent == null) { if (parent == null) {
return false; return false;
@ -294,17 +258,17 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* *
* @return The retrieved data or null if null dataSource. * @return The retrieved data or null if null dataSource.
* *
* @throws SleuthkitCaseProviderException * @throws NoCurrentCaseException
* @throws TskCoreException * @throws TskCoreException
*/ */
public PastCasesResult getPastCasesData(DataSource dataSource) public static PastCasesResult getPastCasesData(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { throws NoCurrentCaseException, TskCoreException {
if (dataSource == null) { if (dataSource == null) {
return null; return null;
} }
SleuthkitCase skCase = caseProvider.get(); SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
List<String> deviceArtifactCases = new ArrayList<>(); List<String> deviceArtifactCases = new ArrayList<>();
List<String> nonDeviceArtifactCases = new ArrayList<>(); List<String> nonDeviceArtifactCases = new ArrayList<>();

View File

@ -0,0 +1,76 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 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.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<Integer> 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<Integer> 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);
}
}
}

View File

@ -22,8 +22,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; import org.sleuthkit.autopsy.contentutils.PastCasesSummary.PastCasesResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummaryGetter;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
@ -80,19 +80,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher; private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher;
public PastCasesPanel() { public PastCasesPanel() {
this(new PastCasesSummary()); this(new PastCasesSummaryGetter());
} }
/** /**
* Creates new form PastCasesPanel * Creates new form PastCasesPanel
*/ */
public PastCasesPanel(PastCasesSummary pastCaseData) { public PastCasesPanel(PastCasesSummaryGetter pastCaseData) {
super(pastCaseData); super(pastCaseData);
this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource); this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource);
// set up data acquisition methods // set up data acquisition methods
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
@ -124,20 +124,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource); onNewDataSource(dataFetchComponents, tables, dataSource);
} }
/* ELTODO
@Override
List<ExcelExport.ExcelSheetExport> 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 @Override
public void close() { public void close() {
ingestRunningLabel.unregister(); ingestRunningLabel.unregister();

View File

@ -33,6 +33,11 @@ ExportContainerInfo_export_timeZone=Time Zone:
ExportContainerInfo_export_unallocatedSize=Unallocated Space: ExportContainerInfo_export_unallocatedSize=Unallocated Space:
ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A
ExportContainerInfo_tabName=Container 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_attachmentsTable_tabName=Recent Attachments
ExportRecentFiles_col_head_date=Date ExportRecentFiles_col_head_date=Date
ExportRecentFiles_col_header_domain=Domain ExportRecentFiles_col_header_domain=Domain

View File

@ -97,6 +97,7 @@ class ExcelExportAction {
"ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data", "ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data",
"ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data", "ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data",
"ExcelExportAction_exportToXLSX_gatheringAnalysisData=Fetching Analysis Data", "ExcelExportAction_exportToXLSX_gatheringAnalysisData=Fetching Analysis Data",
"ExcelExportAction_exportToXLSX_gatheringPastData=Fetching Historical Data",
"ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",}) "ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",})
void exportToXLSX(ReportProgressPanel progressPanel, DataSource dataSource, String baseReportDir) void exportToXLSX(ReportProgressPanel progressPanel, DataSource dataSource, String baseReportDir)
@ -148,6 +149,14 @@ class ExcelExportAction {
if (exports != null) { if (exports != null) {
sheetExports.addAll(exports); 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.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_writingToFile());
progressPanel.setProgress(9); progressPanel.setProgress(9);

View File

@ -0,0 +1,76 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.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<Pair<String, Long>, 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<Pair<String, Long>, 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<ColumnModel<Pair<String, Long>, DefaultCellModel<?>>> DEFAULT_TEMPLATE
= Arrays.asList(CASE_COL, COUNT_COL);
private ExportPastCases() {
}
static List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, PastCasesResult> 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())
);
}
}

View File

@ -40,7 +40,7 @@ import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException; 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. * specified DataSource.
*/ */
@Messages({ @Messages({