diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceAnalysisSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceAnalysisSummary.java new file mode 100644 index 0000000000..86aa21c7a1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceAnalysisSummary.java @@ -0,0 +1,159 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Providing data for the data source analysis tab. + */ +public class DataSourceAnalysisSummary { + + private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME); + + private static final Set EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>(Arrays.asList( + "PHONE NUMBERS", + "IP ADDRESSES", + "EMAIL ADDRESSES", + "URLS", + "CREDIT CARD NUMBERS" + )); + + private final SleuthkitCaseProvider provider; + + /** + * Main constructor. + */ + public DataSourceAnalysisSummary() { + this(SleuthkitCaseProvider.DEFAULT); + } + + /** + * Main constructor. + * + * @param provider The means of obtaining a sleuthkit case. + */ + public DataSourceAnalysisSummary(SleuthkitCaseProvider provider) { + this.provider = provider; + } + + /** + * Gets counts for hashset hits. + * + * @param dataSource The datasource for which to identify hashset hits. + * + * @return The hashset set name with the number of hits in descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT); + } + + /** + * Gets counts for keyword hits. + * + * @param dataSource The datasource for which to identify keyword hits. + * + * @return The keyword set name with the number of hits in descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_KEYWORD_HIT).stream() + // make sure we have a valid set and that that set does not belong to the set of excluded items + .filter((pair) -> pair != null && pair.getKey() != null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(pair.getKey().toUpperCase().trim())) + .collect(Collectors.toList()); + } + + /** + * Gets counts for interesting item hits. + * + * @param dataSource The datasource for which to identify interesting item + * hits. + * + * @return The interesting item set name with the number of hits in + * descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); + } + + /** + * Get counts for the artifact of the specified type. + * + * @param dataSource The datasource. + * @param keyType The attribute to use as the key type. + * @param artifactTypes The types of artifacts for which to query. + * + * @return A list of key value pairs where the key is the attribute type + * value and the value is the count of items found. This list is + * sorted by the count descending max to min. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + private List> getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes) + throws SleuthkitCaseProviderException, TskCoreException { + List artifacts = new ArrayList<>(); + SleuthkitCase skCase = provider.get(); + + // get all artifacts in one list for each artifact type + for (ARTIFACT_TYPE type : artifactTypes) { + artifacts.addAll(skCase.getBlackboard().getArtifacts(type.getTypeID(), dataSource.getId())); + } + + // group those based on the value of the attribute type that should serve as a key + Map countedKeys = artifacts.stream() + .map((art) -> { + String key = DataSourceInfoUtilities.getStringOrNull(art, keyType); + return (StringUtils.isBlank(key)) ? null : key; + }) + .filter((key) -> key != null) + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + + // sort from max to min counts + return countedKeys.entrySet().stream() + .map((e) -> Pair.of(e.getKey(), e.getValue())) + .sorted((a, b) -> -a.getValue().compareTo(b.getValue())) + .collect(Collectors.toList()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form new file mode 100644 index 0000000000..4016b539a7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form @@ -0,0 +1,263 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java new file mode 100644 index 0000000000..16de061385 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -0,0 +1,209 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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.ui; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceAnalysisSummary; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; +import org.sleuthkit.datamodel.DataSource; + +/** + * A tab shown in data source summary displaying hash set hits, keyword hits, + * and interesting item hits within a datasource. + */ +@Messages({ + "AnalysisPanel_keyColumn_title=Name", + "AnalysisPanel_countColumn_title=Count" +}) +public class AnalysisPanel extends BaseDataSourceSummaryPanel { + + private static final long serialVersionUID = 1L; + + /** + * Default Column definitions for each table + */ + private static final List>> DEFAULT_COLUMNS = Arrays.asList( + new ColumnModel<>( + Bundle.AnalysisPanel_keyColumn_title(), + (pair) -> new DefaultCellModel(pair.getKey()), + 300 + ), + new ColumnModel<>( + Bundle.AnalysisPanel_countColumn_title(), + (pair) -> new DefaultCellModel(String.valueOf(pair.getValue())), + 100 + ) + ); + + private final JTablePanel> hashsetHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS); + + private final JTablePanel> keywordHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS); + + private final JTablePanel> interestingItemsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS); + + private final List> tables = Arrays.asList( + hashsetHitsTable, + keywordHitsTable, + interestingItemsTable + ); + + /** + * All of the components necessary for data fetch swing workers to load data + * for each table. + */ + private final List> dataFetchComponents; + + /** + * Creates a new DataSourceUserActivityPanel. + */ + public AnalysisPanel() { + this(new DataSourceAnalysisSummary()); + } + + public AnalysisPanel(DataSourceAnalysisSummary analysisData) { + // set up data acquisition methods + dataFetchComponents = Arrays.asList( + // hashset hits loading components + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> analysisData.getHashsetCounts(dataSource), + (result) -> hashsetHitsTable.showDataFetchResult(result)), + // keyword hits loading components + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> analysisData.getKeywordCounts(dataSource), + (result) -> keywordHitsTable.showDataFetchResult(result)), + // interesting item hits loading components + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> analysisData.getInterestingItemCounts(dataSource), + (result) -> interestingItemsTable.showDataFetchResult(result)) + ); + + initComponents(); + } + + @Override + protected void onNewDataSource(DataSource dataSource) { + // if no data source is present or the case is not open, + // set results for tables to null. + if (dataSource == null || !Case.isCaseOpen()) { + this.dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataFetchResult.getSuccessResult(null))); + + } else { + // set tables to display loading screen + this.tables.forEach((table) -> table.showDefaultLoadingMessage()); + + // create swing workers to run for each table + List> workers = dataFetchComponents + .stream() + .map((components) -> new DataFetchWorker<>(components, dataSource)) + .collect(Collectors.toList()); + + // submit swing workers to run + submit(workers); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane(); + javax.swing.JPanel mainContentPanel = new javax.swing.JPanel(); + javax.swing.JLabel hashsetHitsLabel = new javax.swing.JLabel(); + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); + javax.swing.JPanel hashSetHitsPanel = hashsetHitsTable; + javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); + javax.swing.JLabel keywordHitsLabel = new javax.swing.JLabel(); + javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); + javax.swing.JPanel keywordHitsPanel = keywordHitsTable; + javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); + javax.swing.JLabel interestingItemLabel = new javax.swing.JLabel(); + javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); + javax.swing.JPanel interestingItemPanel = interestingItemsTable; + javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + + mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); + mainContentPanel.setMaximumSize(new java.awt.Dimension(32767, 452)); + mainContentPanel.setMinimumSize(new java.awt.Dimension(200, 452)); + mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS)); + + org.openide.awt.Mnemonics.setLocalizedText(hashsetHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.hashsetHitsLabel.text")); // NOI18N + mainContentPanel.add(hashsetHitsLabel); + mainContentPanel.add(filler1); + + hashSetHitsPanel.setAlignmentX(0.0F); + hashSetHitsPanel.setMaximumSize(new java.awt.Dimension(32767, 106)); + hashSetHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); + hashSetHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); + mainContentPanel.add(hashSetHitsPanel); + mainContentPanel.add(filler2); + + org.openide.awt.Mnemonics.setLocalizedText(keywordHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.keywordHitsLabel.text")); // NOI18N + mainContentPanel.add(keywordHitsLabel); + mainContentPanel.add(filler4); + + keywordHitsPanel.setAlignmentX(0.0F); + keywordHitsPanel.setMaximumSize(new java.awt.Dimension(32767, 106)); + keywordHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); + keywordHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); + mainContentPanel.add(keywordHitsPanel); + mainContentPanel.add(filler5); + + org.openide.awt.Mnemonics.setLocalizedText(interestingItemLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.interestingItemLabel.text")); // NOI18N + mainContentPanel.add(interestingItemLabel); + mainContentPanel.add(filler6); + + interestingItemPanel.setAlignmentX(0.0F); + interestingItemPanel.setMaximumSize(new java.awt.Dimension(32767, 106)); + interestingItemPanel.setMinimumSize(new java.awt.Dimension(10, 106)); + interestingItemPanel.setPreferredSize(new java.awt.Dimension(10, 106)); + mainContentPanel.add(interestingItemPanel); + mainContentPanel.add(filler3); + + mainScrollPane.setViewportView(mainContentPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 756, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index 3d4d3af78c..b88a008e10 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -35,6 +35,9 @@ DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains +AnalysisPanel.hashsetHitsLabel.text=Hashset Hits +AnalysisPanel.keywordHitsLabel.text=Keyword Hits +AnalysisPanel.interestingItemLabel.text=Interesting Item Hits RecentFilesPanel.openDocsLabel.text=Recently Opened Documents RecentFilesPanel.downloadLabel.text=Recent Downloads RecentFilesPanel.attachmentLabel.text=Recent Attachements diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index bd71f58f9c..f3325f2e46 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -1,3 +1,5 @@ +AnalysisPanel_countColumn_title=Count +AnalysisPanel_keyColumn_title=Name CTL_DataSourceSummaryAction=Data Source Summary DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type @@ -64,6 +66,7 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryTabbedPane_analysisTab_title=Analysis DataSourceSummaryTabbedPane_countsTab_title=Counts DataSourceSummaryTabbedPane_detailsTab_title=Container DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History @@ -74,6 +77,9 @@ DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains +AnalysisPanel.hashsetHitsLabel.text=Hashset Hits +AnalysisPanel.keywordHitsLabel.text=Keyword Hits +AnalysisPanel.interestingItemLabel.text=Interesting Item Hits DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 047af1cdd9..9696f4d4c4 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -37,7 +37,8 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryTabbedPane_detailsTab_title=Container", "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History", - "DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files" + "DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files", + "DataSourceSummaryTabbedPane_analysisTab_title=Analysis" }) public class DataSourceSummaryTabbedPane extends JTabbedPane { @@ -47,7 +48,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private final List> tabs = new ArrayList<>(Arrays.asList( Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()), - Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()) + Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), + Pair.of(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()) )); private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();