From b14add6c25764e248a83472007fb3fcc81dae2c7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:54:51 -0400 Subject: [PATCH 01/15] beginnings of top program items --- .../datasourcesummary/Bundle.properties | 1 + .../Bundle.properties-MERGED | 4 + .../datasourcesummary/Bundle_ja.properties | 1 + .../DataSourceInfoUtilities.java | 87 +++++++++++++++---- .../DataSourceSummaryTabbedPane.java | 5 ++ .../autoingest/Bundle.properties-MERGED | 2 - 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties index 2cb4756460..24a312f733 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties @@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.jLabel1.text=Results by Type +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index eb7affe9ee..4682d7d76f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -72,4 +72,8 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run +DataSourceSummaryUserActivityPanel_tab_title=User Activity +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties index 99fd24c7ef..a4e60c4f36 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties @@ -71,3 +71,4 @@ DataSourceSummaryNode.column.tags.header=\u30bf\u30b0 DataSourceSummaryNode.column.type.header=\u30bf\u30a4\u30d7 DataSourceSummaryNode.viewDataSourceAction.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u79fb\u52d5 ViewSummaryInformationAction.name.text=\u30b5\u30de\u30ea\u30fc\u60c5\u5831\u3092\u8868\u793a +DataSourceSummaryUserActivityPanel.programsRunLabel.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 662f3739f0..da991e26fc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019 - 2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -29,6 +28,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Set; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -194,6 +194,33 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, handler, errorMessage); } + /** + * Generates a result set handler that will return a map of string to long. + * + * @param keyParam The named parameter in the result set representing the + * key. + * @param valueParam The named parameter in the result set representing the + * value. + * + * @return The result set handler to generate the map of string to long. + */ + private static ResultSetHandler> getStringLongResultSetHandler(String keyParam, String valueParam) { + ResultSetHandler> handler = (resultSet) -> { + LinkedHashMap toRet = new LinkedHashMap<>(); + while (resultSet.next()) { + try { + toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam)); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); + } + } + + return toRet; + }; + + return handler; + } + /** * Retrieves counts for each artifact type in a data source. * @@ -216,22 +243,50 @@ final class DataSourceInfoUtilities { + " WHERE bba.data_source_obj_id =" + selectedDataSource.getId() + " GROUP BY bbt.display_name"; - ResultSetHandler> handler = (resultSet) -> { - Map toRet = new HashMap<>(); - while (resultSet.next()) { - try { - toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam)); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); - } - } - - return toRet; - }; - String errorMessage = "Unable to get artifact type counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); + } - return getBaseQueryResult(query, handler, errorMessage); + /** + * Retrieves a list of the top programs used on the data source. Currently + * determines this based off of which prefetch results return the highest + * count. + * + * @param dataSource The data source. + * @param count The number of programs to return. + * + * @return + */ + static Map getTopPrograms(DataSource dataSource, int count) { + if (dataSource == null || count <= 0) { + return Collections.emptyMap(); + } + + String progNameParam = "prog_name"; + String runTimesParam = "run_times"; + String prefetchIdentifier = "Windows Prefetch Extractor"; + + String query = "SELECT program_artifacts." + progNameParam + ", MAX(attr.value_int32) AS " + runTimesParam + "\n" + + "FROM blackboard_artifacts bba\n" + + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + "INNER JOIN (\n" + + "-- get all the different artifacts coming from prefetch\n" + + " SELECT \n" + + " bba.artifact_id as artifact_id, \n" + + " attr.value_text as " + progNameParam + "\n" + + " FROM blackboard_artifacts bba\n" + + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + " WHERE bba.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" + + " AND attr.source = '" + prefetchIdentifier + "'\n" + + ") program_artifacts ON bba.artifact_id = program_artifacts.artifact_id\n" + + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" + + "GROUP BY program_artifacts." + progNameParam + "\n" + + "ORDER BY " + runTimesParam + " DESC\n" + + "LIMIT " + count + " OFFSET 0"; + + String errorMessage = "Unable to get top program counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(progNameParam, runTimesParam), errorMessage); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 8ef4d62c5d..78dc38190f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -33,6 +33,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; + private final DataSourceSummaryUserActivityPanel userActivityPanel; // ingest panel requires an open case in order to properly initialize. // So it will be instantiated when a data source is selected. @@ -46,6 +47,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); + userActivityPanel = new DataSourceSummaryUserActivityPanel(); } /** @@ -63,6 +65,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); countsPanel.setDataSource(dataSource); + addTab(Bundle.DataSourceSummaryUserActivityPanel_tab_title(), userActivityPanel); + userActivityPanel.setDataSource(dataSource); + if (ingestHistoryPanel == null) { ingestHistoryPanel = new IngestJobInfoPanel(); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 823399e0d0..56a675e256 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,9 +205,7 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path From 5c9f1a8c9f609ca12c9508a83eadf73844d61f50 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:56:36 -0400 Subject: [PATCH 02/15] beginnings of top program items --- .../DataSourceSummaryUserActivityPanel.form | 80 +++++++++ .../DataSourceSummaryUserActivityPanel.java | 166 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form new file mode 100644 index 0000000000..02cda9a4b3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -0,0 +1,80 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java new file mode 100644 index 0000000000..f556ebc7f3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -0,0 +1,166 @@ +/* + * 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.casemodule.datasourcesummary; + +import java.util.Map; +import javax.swing.JLabel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.DataSource; + +/** + * A panel to display user activity. + */ +@Messages({ + "DataSourceSummaryUserActivityPanel_tab_title=User Activity", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" +}) +public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { + // Result returned for a data model if no data found. + private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final int TOP_PROGS_COUNT = 10; + + private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); + + static { + RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); + } + + // column headers for artifact counts table + private static final Object[] TOP_PROGS_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + }; + + private DataSource dataSource; + + /** + * Creates new form DataSourceUserActivityPanel + */ + public DataSourceSummaryUserActivityPanel() { + initComponents(); + } + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + public DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + if (dataSource == null || !Case.isCaseOpen()) { + updateTopPrograms(EMPTY_PAIRS); + } else { + updateTopPrograms(getTopProgramsModel(dataSource)); + } + } + + /** + * Updates the Top Programs Table in the gui. + * @param data The data in Object[][] form to be used by the DefaultTableModel. + */ + private void updateTopPrograms(Object[][] data) { + topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); + topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + this.repaint(); + } + + /** + * The counts of top programs run. + * + * @param selectedDataSource The DataSource. + * + * @return The JTable data model of counts of program runs. + */ + private static Object[][] getTopProgramsModel(DataSource selectedDataSource) { + Map artifactMapping = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + if (artifactMapping == null) { + return EMPTY_PAIRS; + } + + return artifactMapping.entrySet().stream() + .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) + .sorted((a, b) -> -Long.compare(a.getValue(), b.getValue())) + .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) + .toArray(Object[][]::new); + } + + + /** + * 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.JLabel programsRunLabel = new javax.swing.JLabel(); + javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); + topProgramsTable = new javax.swing.JTable(); + + setMinimumSize(new java.awt.Dimension(256, 300)); + + org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N + + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); + + topProgramsTable.setAutoCreateRowSorter(true); + topProgramsScrollPane.setViewportView(topProgramsTable); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(47, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(programsRunLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTable topProgramsTable; + // End of variables declaration//GEN-END:variables +} From 11fc7e24d2d0b8151111420190eeeeca8ea1074b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 11:46:44 -0400 Subject: [PATCH 03/15] worked through some table editing issues --- .../DataSourceSummaryUserActivityPanel.form | 3 --- .../DataSourceSummaryUserActivityPanel.java | 5 ++--- .../communications/relationships/Bundle.properties-MERGED | 2 +- .../core.jar/org/netbeans/core/startup/Bundle.properties | 2 +- .../org/netbeans/core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 02cda9a4b3..0ad3ca0349 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -70,9 +70,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index f556ebc7f3..696e6e904b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -58,6 +58,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { */ public DataSourceSummaryUserActivityPanel() { initComponents(); + topProgramsTable.getTableHeader().setReorderingAllowed(false); } /** @@ -88,7 +89,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @param data The data in Object[][] form to be used by the DefaultTableModel. */ private void updateTopPrograms(Object[][] data) { - topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.setModel(new NonEditableTableModel(data, TOP_PROGS_COLUMN_HEADERS)); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); @@ -133,8 +134,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); - - topProgramsTable.setAutoCreateRowSorter(true); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 56a675e256..823399e0d0 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,7 +205,9 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path 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 333baeeca2..35138509d8 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 -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 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 044fad190f..cf36e85b33 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 -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 CTL_MainWindow_Title=Autopsy 4.16.0 CTL_MainWindow_Title_No_Project=Autopsy 4.16.0 From adbaf3f70a9abbaaaaa2dd86215596c4bbaff4ea Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 07:34:23 -0400 Subject: [PATCH 05/15] some refactoring --- .../DataSourceSummaryDialog.java | 5 +--- .../DataSourceSummaryTabbedPane.java | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index e1197fa83a..9cd7549c06 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -49,10 +49,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser * datasource. */ @Messages({ - "DataSourceSummaryDialog.window.title=Data Sources Summary", - "DataSourceSummaryDialog.countsTab.title=Counts", - "DataSourceSummaryDialog.detailsTab.title=Details", - "DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History" + "DataSourceSummaryDialog.window.title=Data Sources Summary" }) DataSourceSummaryDialog(Frame owner) { super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index e1d15a6b0d..c112517acc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.JTabbedPane; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; @@ -27,13 +28,20 @@ import org.sleuthkit.datamodel.DataSource; * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and * IngestJobInfoPanel. */ +@Messages({ + "DataSourceSummaryTabbedPane_countsTab_title=Counts", + "DataSourceSummaryTabbedPane_detailsTab_title=Details", + "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", + "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" +}) public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - private final DataSourceSummaryCountsPanel countsPanel; - private final DataSourceSummaryDetailsPanel detailsPanel; - private final IngestJobInfoPanel ingestHistoryPanel; + private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel(); + private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel(); + private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel(); + private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); private DataSource dataSource = null; @@ -41,13 +49,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { - countsPanel = new DataSourceSummaryCountsPanel(); - detailsPanel = new DataSourceSummaryDetailsPanel(); - ingestHistoryPanel = new IngestJobInfoPanel(); - - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + + addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel); } /** @@ -69,6 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { detailsPanel.setDataSource(dataSource); countsPanel.setDataSource(dataSource); + userActivityPanel.setDataSource(dataSource); ingestHistoryPanel.setDataSource(dataSource); } } From b637d4d2c1c5ea2ce96d36b105c768c52e013d9f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 09:52:06 -0400 Subject: [PATCH 06/15] updating for handling path --- .../DataSourceInfoUtilities.java | 129 +++++++++++++++--- .../DataSourceSummaryUserActivityPanel.java | 80 ++++++++--- 2 files changed, 170 insertions(+), 39 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 511d1a62a2..7ca82753b0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -30,6 +32,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import org.apache.commons.lang3.tuple.Pair; +import java.util.function.Function; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -247,6 +253,31 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); } + static class TopProgramsResult { + + private final String programName; + private final String programPath; + private final long runTimes; + + public TopProgramsResult(String programName, String programPath, long runTimes) { + this.programName = programName; + this.programPath = programPath; + this.runTimes = runTimes; + } + + public String getProgramName() { + return programName; + } + + public String getProgramPath() { + return programPath; + } + + public long getRunTimes() { + return runTimes; + } + } + /** * Retrieves a list of the top programs used on the data source. Currently * determines this based off of which prefetch results return the highest @@ -257,36 +288,94 @@ final class DataSourceInfoUtilities { * * @return */ - static Map getTopPrograms(DataSource dataSource, int count) { + static List getTopPrograms(DataSource dataSource, int count) { if (dataSource == null || count <= 0) { - return Collections.emptyMap(); + return Collections.emptyList(); } - String progNameParam = "prog_name"; - String runTimesParam = "run_times"; - String prefetchIdentifier = "Windows Prefetch Extractor"; + final String progNameParam = "prog_name"; + final String progPathParam = "prog_path"; + final String runTimesParam = "run_times"; + final String prefetchIdentifier = "Windows Prefetch Extractor"; - String query = "SELECT program_artifacts." + progNameParam + ", MAX(attr.value_int32) AS " + runTimesParam + "\n" + final String query = "SELECT\n" + + " prog_name_query." + progNameParam + ",\n" + + " prog_path_query." + progPathParam + ",\n" + + " MAX(attr.value_int32) AS " + runTimesParam + "\n" + "FROM blackboard_artifacts bba\n" + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + "INNER JOIN (\n" - + "-- get all the different artifacts coming from prefetch\n" - + " SELECT \n" - + " bba.artifact_id as artifact_id, \n" - + " attr.value_text as " + progNameParam + "\n" - + " FROM blackboard_artifacts bba\n" - + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" - + " WHERE bba.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" - + " AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" - + " AND attr.source = '" + prefetchIdentifier + "'\n" - + ") program_artifacts ON bba.artifact_id = program_artifacts.artifact_id\n" + + " SELECT \n" + + " bba1.artifact_id AS artifact_id,\n" + + " attr1.value_text AS " + progNameParam + "\n" + + " FROM blackboard_artifacts bba1\n" + + " INNER JOIN blackboard_attributes attr1 ON bba1.artifact_id = attr1.artifact_id\n" + + " WHERE bba1.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" + + ") prog_name_query ON bba.artifact_id = prog_name_query.artifact_id\n" + + "LEFT JOIN (\n" + + " SELECT \n" + + " bba2.artifact_id AS artifact_id,\n" + + " attr2.value_text AS " + progPathParam + "\n" + + " FROM blackboard_artifacts bba2\n" + + " INNER JOIN blackboard_attributes attr2 ON bba2.artifact_id = attr2.artifact_id\n" + + " WHERE bba2.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID() + "\n" + + ") prog_path_query ON bba.artifact_id = prog_path_query.artifact_id\n" + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" - + "GROUP BY program_artifacts." + progNameParam + "\n" - + "ORDER BY " + runTimesParam + " DESC\n" + + "AND bba.data_source_obj_id = " + dataSource.getId() + "\n" + + "AND attr.source = '" + prefetchIdentifier + "'\n" + + "GROUP BY prog_name_query." + progNameParam + ", prog_path_query." + progPathParam + "\n" + + "ORDER BY MAX(attr.value_int32) DESC\n" + "LIMIT " + count + " OFFSET 0"; - String errorMessage = "Unable to get top program counts; returning null."; - return getBaseQueryResult(query, getStringLongResultSetHandler(progNameParam, runTimesParam), errorMessage); + final String errorMessage = "Unable to get top program results; returning null."; + + ResultSetHandler> handler = (resultSet) -> { + List progResults = new ArrayList<>(); + + while (resultSet.next()) { + try { + progResults.add(new TopProgramsResult( + resultSet.getString(progNameParam), + resultSet.getString(progPathParam), + resultSet.getLong(runTimesParam))); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex); + } + } + + return progResults; + }; + + return getBaseQueryResult(query, handler, errorMessage); + } + + private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( + // Windows if in /Windows + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), + // program name if /Program Files/program or /Program Files (x86)/program + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)[\\\\\\/]", + Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), + // match for an AppData folder + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)"), (match) -> "AppData") + ); + + static String getShortFolderName(String path) { + if (path == null) { + return ""; + } + + for (Pair> matchEntry : SHORT_FOLDER_MATCHERS) { + Pattern p = matchEntry.getLeft(); + Function resultsProvider = matchEntry.getRight(); + Matcher match = p.matcher(path); + if (match.find()) { + return resultsProvider.apply(match); + } + } + + return ""; } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 696e6e904b..1b0abc05fa 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -18,10 +18,13 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import javax.swing.JLabel; +import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.DataSource; @@ -32,6 +35,7 @@ import org.sleuthkit.datamodel.DataSource; @Messages({ "DataSourceSummaryUserActivityPanel_tab_title=User Activity", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { @@ -45,12 +49,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - // column headers for artifact counts table - private static final Object[] TOP_PROGS_COLUMN_HEADERS = new Object[]{ - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() - }; - private DataSource dataSource; /** @@ -78,7 +76,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; if (dataSource == null || !Case.isCaseOpen()) { - updateTopPrograms(EMPTY_PAIRS); + updateTopPrograms(new TopProgramsModel(null)); } else { updateTopPrograms(getTopProgramsModel(dataSource)); } @@ -88,8 +86,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * Updates the Top Programs Table in the gui. * @param data The data in Object[][] form to be used by the DefaultTableModel. */ - private void updateTopPrograms(Object[][] data) { - topProgramsTable.setModel(new NonEditableTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + private void updateTopPrograms(TopProgramsModel model) { + topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); @@ -102,17 +100,61 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * * @return The JTable data model of counts of program runs. */ - private static Object[][] getTopProgramsModel(DataSource selectedDataSource) { - Map artifactMapping = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - if (artifactMapping == null) { - return EMPTY_PAIRS; + private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { + List topProgramList = + DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + + if (topProgramList == null) { + return new TopProgramsModel(null); + } else { + return new TopProgramsModel(topProgramList); + } + } + + private static class TopProgramsModel extends AbstractTableModel { + // column headers for artifact counts table + private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + }; + + private final List programResults; + + public TopProgramsModel(List programResults) { + this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } - return artifactMapping.entrySet().stream() - .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) - .sorted((a, b) -> -Long.compare(a.getValue(), b.getValue())) - .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) - .toArray(Object[][]::new); + @Override + public String getColumnName(int column) { + return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; + } + + @Override + public int getRowCount() { + return programResults.size(); + } + + @Override + public int getColumnCount() { + return TOP_PROGS_COLUMN_HEADERS.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (rowIndex < 0 || rowIndex >= programResults.size()) { + return null; + } + + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); + switch (columnIndex) { + case 0: return result.getProgramName(); + case 1: return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); + case 2: return result.getRunTimes(); + default: return null; + } + } + } From d79fa1f2a04321461458e753e14e0a935c4cdcb0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 11:04:27 -0400 Subject: [PATCH 07/15] working through top programs data model --- .../Bundle.properties-MERGED | 8 +- .../DataSourceSummaryUserActivityPanel.java | 96 ++++++++++++++----- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index 4682d7d76f..40e5bf2386 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -61,9 +61,6 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.jLabel1.text=Results by Type -DataSourceSummaryDialog.countsTab.title=Counts -DataSourceSummaryDialog.detailsTab.title=Details -DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History DataSourceSummaryDialog.window.title=Data Sources Summary DataSourceSummaryNode.column.dataSourceName.header=Data Source Name DataSourceSummaryNode.column.files.header=Files @@ -72,8 +69,13 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryTabbedPane_countsTab_title=Counts +DataSourceSummaryTabbedPane_detailsTab_title=Details +DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History +DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 1b0abc05fa..7b00d8f527 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -18,13 +18,15 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.awt.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import javax.swing.JLabel; +import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.DataSource; @@ -39,18 +41,16 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { - // Result returned for a data model if no data found. - private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final int TOP_PROGS_COUNT = 10; - private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); - + static { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - + private DataSource dataSource; - + /** * Creates new form DataSourceUserActivityPanel */ @@ -81,18 +81,21 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { updateTopPrograms(getTopProgramsModel(dataSource)); } } - + /** * Updates the Top Programs Table in the gui. - * @param data The data in Object[][] form to be used by the DefaultTableModel. + * + * @param data The data in Object[][] form to be used by the + * DefaultTableModel. */ private void updateTopPrograms(TopProgramsModel model) { topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); - topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); + topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); } - + /** * The counts of top programs run. * @@ -101,24 +104,64 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @return The JTable data model of counts of program runs. */ private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { - List topProgramList = - DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - + List topProgramList + = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + if (topProgramList == null) { return new TopProgramsModel(null); } else { return new TopProgramsModel(topProgramList); } } - + + private static class ProgramNameCellValue { + + private final String programName; + private final String programPath; + + public ProgramNameCellValue(String programName, String programPath) { + this.programName = programName; + this.programPath = programPath; + } + + @Override + public String toString() { + return programName; + } + + String getProgramName() { + return programName; + } + + String getProgramPath() { + return programPath; + } + } + + private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { + + public Component getTableCellRendererComponent( + JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (value instanceof ProgramNameCellValue) { + ProgramNameCellValue cellValue = (ProgramNameCellValue) value; + c.setToolTipText(cellValue.getProgramPath()); + } + return c; + } + }; + private static class TopProgramsModel extends AbstractTableModel { + // column headers for artifact counts table private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() }; - + private final List programResults; public TopProgramsModel(List programResults) { @@ -129,7 +172,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { public String getColumnName(int column) { return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; } - + @Override public int getRowCount() { return programResults.size(); @@ -145,19 +188,22 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { if (rowIndex < 0 || rowIndex >= programResults.size()) { return null; } - + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); switch (columnIndex) { - case 0: return result.getProgramName(); - case 1: return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); - case 2: return result.getRunTimes(); - default: return null; + case 0: + return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath()); + case 1: + return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); + case 2: + return result.getRunTimes(); + default: + return null; } } - + } - - + /** * 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 From b700d17011697371c4db827140c967a8fb94502f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 12:05:25 -0400 Subject: [PATCH 08/15] now with fixed paths and tooltips --- .../datasourcesummary/DataSourceInfoUtilities.java | 10 +++++++--- .../DataSourceSummaryUserActivityPanel.form | 11 +++++++---- .../DataSourceSummaryUserActivityPanel.java | 12 +++++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 7ca82753b0..92f0723af3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -353,12 +353,16 @@ final class DataSourceInfoUtilities { private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( // Windows if in /Windows - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), + (match) -> "Windows"), + // program name if /Program Files/program or /Program Files (x86)/program - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)[\\\\\\/]", + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), + // match for an AppData folder - Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)"), (match) -> "AppData") + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), + (match) -> "AppData") ); static String getShortFolderName(String path) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 0ad3ca0349..08a49c17e4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -3,7 +3,10 @@
- + + + + @@ -25,9 +28,9 @@ - + - + @@ -58,7 +61,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 7b00d8f527..e6befe9e9c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -92,7 +92,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); - topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150); + topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); } @@ -217,11 +218,12 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); topProgramsTable = new javax.swing.JTable(); - setMinimumSize(new java.awt.Dimension(256, 300)); + setMinimumSize(null); + setPreferredSize(null); org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N - topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(460, 187)); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); @@ -232,8 +234,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(47, Short.MAX_VALUE)) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(128, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) From f7985fbb0a7b422506a3975e0c3f8a2e362c4416 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 12:08:36 -0400 Subject: [PATCH 09/15] formatting --- .../datasourcesummary/DataSourceInfoUtilities.java | 12 +++++------- .../DataSourceSummaryTabbedPane.java | 12 ++++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 92f0723af3..688bcb86c4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -353,15 +353,13 @@ final class DataSourceInfoUtilities { private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( // Windows if in /Windows - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), - // program name if /Program Files/program or /Program Files (x86)/program - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), - // match for an AppData folder - Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> "AppData") ); @@ -369,7 +367,7 @@ final class DataSourceInfoUtilities { if (path == null) { return ""; } - + for (Pair> matchEntry : SHORT_FOLDER_MATCHERS) { Pattern p = matchEntry.getLeft(); Function resultsProvider = matchEntry.getRight(); @@ -378,7 +376,7 @@ final class DataSourceInfoUtilities { return resultsProvider.apply(match); } } - + return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index c112517acc..dddc4ee123 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -29,15 +29,15 @@ import org.sleuthkit.datamodel.DataSource; * IngestJobInfoPanel. */ @Messages({ - "DataSourceSummaryTabbedPane_countsTab_title=Counts", - "DataSourceSummaryTabbedPane_detailsTab_title=Details", - "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", - "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" + "DataSourceSummaryTabbedPane_countsTab_title=Counts", + "DataSourceSummaryTabbedPane_detailsTab_title=Details", + "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", + "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" }) public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - + private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel(); private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel(); private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel(); @@ -49,7 +49,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { - + addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel); addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel); addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel); From 59430a06af5ffb1291974a930b03dae0331747b4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 7 Aug 2020 13:19:10 -0400 Subject: [PATCH 10/15] revamped query --- .../Bundle.properties-MERGED | 1 + .../DataSourceInfoUtilities.java | 261 +++++++++++++----- .../DataSourceSummaryUserActivityPanel.form | 10 +- .../DataSourceSummaryUserActivityPanel.java | 23 +- 4 files changed, 215 insertions(+), 80 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index 40e5bf2386..ba0d3510c7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -77,5 +77,6 @@ DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 688bcb86c4..50056bbe3e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -18,10 +18,14 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -29,6 +33,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; @@ -36,11 +41,16 @@ import java.util.regex.Pattern; import java.util.regex.Matcher; import org.apache.commons.lang3.tuple.Pair; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.DataSource; /** @@ -51,6 +61,7 @@ final class DataSourceInfoUtilities { private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); + /** * Gets a count of files for a particular datasource where it is not a * virtual directory and has a name. @@ -253,30 +264,108 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); } + /** + * Describes a result of a program run on a datasource. + */ static class TopProgramsResult { private final String programName; private final String programPath; - private final long runTimes; + private final Long runTimes; + private final Date lastRun; - public TopProgramsResult(String programName, String programPath, long runTimes) { + /** + * Main constructor. + * @param programName The name of the program. + * @param programPath The path of the program. + * @param runTimes The number of runs. + */ + TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { this.programName = programName; this.programPath = programPath; this.runTimes = runTimes; + this.lastRun = lastRun; } - public String getProgramName() { + /** + * @return The name of the program + */ + String getProgramName() { return programName; } - public String getProgramPath() { + /** + * @return The path of the program. + */ + String getProgramPath() { return programPath; } - public long getRunTimes() { + /** + * @return The number of run times or null if not present. + */ + Long getRunTimes() { return runTimes; } + + /** + * @return The last time the program was run or null if not present. + */ + public Date getLastRun() { + return lastRun; + } } + + + private enum JoinType { + LEFT, + RIGHT, + INNER, + OUTER + } + + private enum AttributeColumn { + value_text, + value_int32, + value_int64 + } + + private static final String QUERY_SUFFIX = "_query"; + + private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, ATTRIBUTE_TYPE attrType, String keyName, String bbaName) { + String queryName = keyName + QUERY_SUFFIX; + String innerQueryName = "inner_attribute_" + queryName; + + return + "\n" + joinType + " JOIN (\n" + + " SELECT \n" + + " " + innerQueryName + ".artifact_id,\n" + + " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n" + + " FROM blackboard_attributes " + innerQueryName + "\n" + + " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n" + + ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n"; + } + + private static String getFullKey(String key) { + return key + QUERY_SUFFIX + "." + key; + } + + private static String getWhereString(List clauses) { + if (clauses.size() <= 0) { + return ""; + } + + List parenthesized = clauses.stream() + .map(c -> "(" + c + ")") + .collect(Collectors.toList()); + + return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n"; + } + + private static String getLikeClause(String column, String likeString, boolean isLike) { + return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'"; + } + /** * Retrieves a list of the top programs used on the data source. Currently @@ -293,53 +382,69 @@ final class DataSourceInfoUtilities { return Collections.emptyList(); } - final String progNameParam = "prog_name"; - final String progPathParam = "prog_path"; - final String runTimesParam = "run_times"; - final String prefetchIdentifier = "Windows Prefetch Extractor"; + // ntosboot should be ignored + final String ntosBootIdentifier = "NTOSBOOT"; + // programs in windows directory to be ignored + final String windowsDir = "/WINDOWS%"; + + final String nameParam = "name"; + final String pathParam = "path"; + final String runCountParam = "run_count"; + final String lastRunParam = "last_run"; + + String bbaQuery = "bba"; final String query = "SELECT\n" - + " prog_name_query." + progNameParam + ",\n" - + " prog_path_query." + progPathParam + ",\n" - + " MAX(attr.value_int32) AS " + runTimesParam + "\n" - + "FROM blackboard_artifacts bba\n" - + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" - + "INNER JOIN (\n" - + " SELECT \n" - + " bba1.artifact_id AS artifact_id,\n" - + " attr1.value_text AS " + progNameParam + "\n" - + " FROM blackboard_artifacts bba1\n" - + " INNER JOIN blackboard_attributes attr1 ON bba1.artifact_id = attr1.artifact_id\n" - + " WHERE bba1.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" - + " AND attr1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" - + ") prog_name_query ON bba.artifact_id = prog_name_query.artifact_id\n" - + "LEFT JOIN (\n" - + " SELECT \n" - + " bba2.artifact_id AS artifact_id,\n" - + " attr2.value_text AS " + progPathParam + "\n" - + " FROM blackboard_artifacts bba2\n" - + " INNER JOIN blackboard_attributes attr2 ON bba2.artifact_id = attr2.artifact_id\n" - + " WHERE bba2.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" - + " AND attr2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID() + "\n" - + ") prog_path_query ON bba.artifact_id = prog_path_query.artifact_id\n" - + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" - + "AND bba.data_source_obj_id = " + dataSource.getId() + "\n" - + "AND attr.source = '" + prefetchIdentifier + "'\n" - + "GROUP BY prog_name_query." + progNameParam + ", prog_path_query." + progPathParam + "\n" - + "ORDER BY MAX(attr.value_int32) DESC\n" - + "LIMIT " + count + " OFFSET 0"; - + + " " + getFullKey(nameParam) + " AS " + nameParam + ",\n" + + " " + getFullKey(pathParam) + " AS " + pathParam + ",\n" + + " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n" + + " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n" + + "FROM blackboard_artifacts " + bbaQuery + "\n" + + getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery) + + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery) + + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery) + + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery) + + getWhereString(Arrays.asList( + bbaQuery + ".artifact_type_id = " + ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), + bbaQuery + ".data_source_obj_id = " + dataSource.getId(), + // exclude ntosBootIdentifier from results + getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false), + // exclude windows directory items from results + getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false) + )) + + "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n" + + "ORDER BY \n" + + " MAX(" + getFullKey(runCountParam) + ") DESC,\n" + + " MAX(" + getFullKey(lastRunParam) + ") DESC,\n" + + " " + getFullKey(nameParam) + " ASC"; + final String errorMessage = "Unable to get top program results; returning null."; ResultSetHandler> handler = (resultSet) -> { List progResults = new ArrayList<>(); - while (resultSet.next()) { + boolean quitAtCount = false; + + while (resultSet.next() && (!quitAtCount || progResults.size() < count)) { try { + long lastRunEpoch = resultSet.getLong(lastRunParam); + Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000); + + Long runCount = resultSet.getLong(runCountParam); + if (resultSet.wasNull()) { + runCount = null; + } + + if (lastRun != null || runCount != null) { + quitAtCount = true; + } + progResults.add(new TopProgramsResult( - resultSet.getString(progNameParam), - resultSet.getString(progPathParam), - resultSet.getLong(runTimesParam))); + resultSet.getString(nameParam), + resultSet.getString(pathParam), + runCount, + lastRun)); + } catch (SQLException ex) { logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex); } @@ -350,30 +455,62 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, handler, errorMessage); } - - private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( - // Windows if in /Windows - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), - (match) -> "Windows"), - // program name if /Program Files/program or /Program Files (x86)/program - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", - Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), - // match for an AppData folder - Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), - (match) -> "AppData") + + + + /** + * Functions that determine the folder name of a list of path elements. If not matched, function returns null. + */ + private static final List, String>> SHORT_FOLDER_MATCHERS = Arrays.asList( + // handle Program Files and Program Files (x86) - if true, return the next folder + (pathList) -> { + if (pathList.size() < 2) { + return null; + } + + String rootParent = pathList.get(0).toUpperCase(); + if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) { + return pathList.get(1); + } else { + return null; + } + }, + // if there is a folder named "APPLICATION DATA" or "APPDATA" + (pathList) -> { + for (String pathEl : pathList) { + String uppered = pathEl.toUpperCase(); + if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) { + return "AppData"; + } + } + return null; + } ); - static String getShortFolderName(String path) { - if (path == null) { + /** + * Determines a short folder name if any. Otherwise, returns empty string. + * @param strPath The string path. + * @return The short folder name or empty string if not found. + */ + static String getShortFolderName(String strPath, String applicationName) { + if (strPath == null) { return ""; } - for (Pair> matchEntry : SHORT_FOLDER_MATCHERS) { - Pattern p = matchEntry.getLeft(); - Function resultsProvider = matchEntry.getRight(); - Matcher match = p.matcher(path); - if (match.find()) { - return resultsProvider.apply(match); + List pathEls = new ArrayList<>(Arrays.asList(applicationName)); + + File file = new File(strPath); + while (file != null && StringUtils.isNotBlank(file.getName())) { + pathEls.add(file.getName()); + file = file.getParentFile(); + } + + Collections.reverse(pathEls); + + for (Function, String> matchEntry : SHORT_FOLDER_MATCHERS) { + String result = matchEntry.apply(pathEls); + if (StringUtils.isNotBlank(result)) { + return result; } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 08a49c17e4..fe6637fac3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -1,14 +1,6 @@ - - - - - - - - @@ -61,7 +53,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index e6befe9e9c..b98fa2a9b0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -19,6 +19,8 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Component; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -38,10 +40,11 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_tab_title=User Activity", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", - "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { - + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); private static final int TOP_PROGS_COUNT = 10; private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); @@ -90,10 +93,12 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { */ private void updateTopPrograms(TopProgramsModel model) { topProgramsTable.setModel(model); - topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); + topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(250); topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150); topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER); + topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80); + topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150); this.repaint(); } @@ -160,7 +165,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header() }; private final List programResults; @@ -195,9 +201,11 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { case 0: return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath()); case 1: - return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); + return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath(), result.getProgramName()); case 2: return result.getRunTimes(); + case 3: + return result.getLastRun() == null ? null : DATETIME_FORMAT.format(result.getLastRun()); default: return null; } @@ -218,12 +226,9 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); topProgramsTable = new javax.swing.JTable(); - setMinimumSize(null); - setPreferredSize(null); - org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N - topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(460, 187)); + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(750, 187)); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); From f56b66a8ed69d0dc77301d50e31d5ce97abdccf3 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 7 Aug 2020 14:15:46 -0400 Subject: [PATCH 11/15] commenting, formatting, and imports --- .../DataSourceInfoUtilities.java | 132 ++++++++++++------ .../DataSourceSummaryUserActivityPanel.java | 41 +++--- 2 files changed, 107 insertions(+), 66 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 50056bbe3e..85c827950c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -19,13 +19,10 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -37,12 +34,8 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import org.apache.commons.lang3.tuple.Pair; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -61,7 +54,6 @@ final class DataSourceInfoUtilities { private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); - /** * Gets a count of files for a particular datasource where it is not a * virtual directory and has a name. @@ -276,9 +268,10 @@ final class DataSourceInfoUtilities { /** * Main constructor. + * * @param programName The name of the program. * @param programPath The path of the program. - * @param runTimes The number of runs. + * @param runTimes The number of runs. */ TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { this.programName = programName; @@ -315,57 +308,103 @@ final class DataSourceInfoUtilities { return lastRun; } } - - + + /** + * A SQL join type. + */ private enum JoinType { LEFT, RIGHT, INNER, OUTER } - + + /** + * A blackboard attribute value column. + */ private enum AttributeColumn { value_text, value_int32, value_int64 } - + + /** + * The suffix joined to a key name for use as an identifier of a query. + */ private static final String QUERY_SUFFIX = "_query"; - + + /** + * Creates a sql statement querying the blackboard attributes table for a + * particular attribute type and returning a specified value. That query + * also joins with the blackboard artifact table. + * + * @param joinType The type of join statement to create. + * @param attributeColumn The blackboard attribute column that should be + * returned. + * @param attrType The attribute type to query for. + * @param keyName The aliased name of the attribute to return. This + * is also used to calculate the alias of the query + * same as getFullKey. + * @param bbaName The blackboard artifact table alias. + * + * @return The generated sql statement. + */ private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, ATTRIBUTE_TYPE attrType, String keyName, String bbaName) { String queryName = keyName + QUERY_SUFFIX; String innerQueryName = "inner_attribute_" + queryName; - - return - "\n" + joinType + " JOIN (\n" + - " SELECT \n" + - " " + innerQueryName + ".artifact_id,\n" + - " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n" + - " FROM blackboard_attributes " + innerQueryName + "\n" + - " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n" + - ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n"; + + return "\n" + joinType + " JOIN (\n" + + " SELECT \n" + + " " + innerQueryName + ".artifact_id,\n" + + " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n" + + " FROM blackboard_attributes " + innerQueryName + "\n" + + " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n" + + ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n"; } - + + /** + * Given a column key, creates the full name for the column key. + * + * @param key The column key. + * + * @return The full identifier for the column key. + */ private static String getFullKey(String key) { return key + QUERY_SUFFIX + "." + key; } - + + /** + * Constructs a SQL 'where' statement from a list of clauses and puts + * parenthesis around each clause. + * + * @param clauses The clauses + * + * @return The generated 'where' statement. + */ private static String getWhereString(List clauses) { if (clauses.size() <= 0) { return ""; } - + List parenthesized = clauses.stream() .map(c -> "(" + c + ")") .collect(Collectors.toList()); - + return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n"; } - + + /** + * Generates a [column] LIKE sql clause. + * + * @param column The column identifier. + * @param likeString The string that will be used as column comparison. + * @param isLike if false, the statement becomes NOT LIKE. + * + * @return The generated statement. + */ private static String getLikeClause(String column, String likeString, boolean isLike) { return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'"; } - /** * Retrieves a list of the top programs used on the data source. Currently @@ -386,12 +425,12 @@ final class DataSourceInfoUtilities { final String ntosBootIdentifier = "NTOSBOOT"; // programs in windows directory to be ignored final String windowsDir = "/WINDOWS%"; - + final String nameParam = "name"; final String pathParam = "path"; final String runCountParam = "run_count"; final String lastRunParam = "last_run"; - + String bbaQuery = "bba"; final String query = "SELECT\n" @@ -417,34 +456,34 @@ final class DataSourceInfoUtilities { + " MAX(" + getFullKey(runCountParam) + ") DESC,\n" + " MAX(" + getFullKey(lastRunParam) + ") DESC,\n" + " " + getFullKey(nameParam) + " ASC"; - + final String errorMessage = "Unable to get top program results; returning null."; ResultSetHandler> handler = (resultSet) -> { List progResults = new ArrayList<>(); boolean quitAtCount = false; - + while (resultSet.next() && (!quitAtCount || progResults.size() < count)) { try { long lastRunEpoch = resultSet.getLong(lastRunParam); Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000); - + Long runCount = resultSet.getLong(runCountParam); if (resultSet.wasNull()) { runCount = null; } - + if (lastRun != null || runCount != null) { quitAtCount = true; } - + progResults.add(new TopProgramsResult( resultSet.getString(nameParam), resultSet.getString(pathParam), runCount, lastRun)); - + } catch (SQLException ex) { logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex); } @@ -455,11 +494,10 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, handler, errorMessage); } - - - + /** - * Functions that determine the folder name of a list of path elements. If not matched, function returns null. + * Functions that determine the folder name of a list of path elements. If + * not matched, function returns null. */ private static final List, String>> SHORT_FOLDER_MATCHERS = Arrays.asList( // handle Program Files and Program Files (x86) - if true, return the next folder @@ -467,7 +505,7 @@ final class DataSourceInfoUtilities { if (pathList.size() < 2) { return null; } - + String rootParent = pathList.get(0).toUpperCase(); if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) { return pathList.get(1); @@ -481,15 +519,17 @@ final class DataSourceInfoUtilities { String uppered = pathEl.toUpperCase(); if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) { return "AppData"; - } + } } return null; } ); /** - * Determines a short folder name if any. Otherwise, returns empty string. + * Determines a short folder name if any. Otherwise, returns empty string. + * * @param strPath The string path. + * * @return The short folder name or empty string if not found. */ static String getShortFolderName(String strPath, String applicationName) { @@ -498,13 +538,13 @@ final class DataSourceInfoUtilities { } List pathEls = new ArrayList<>(Arrays.asList(applicationName)); - + File file = new File(strPath); while (file != null && StringUtils.isNotBlank(file.getName())) { pathEls.add(file.getName()); file = file.getParentFile(); } - + Collections.reverse(pathEls); for (Function, String> matchEntry : SHORT_FOLDER_MATCHERS) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index b98fa2a9b0..63822ec4ea 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -44,14 +44,15 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); private static final int TOP_PROGS_COUNT = 10; private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); - + static { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - + private DataSource dataSource; /** @@ -112,40 +113,40 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { List topProgramList = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - + if (topProgramList == null) { return new TopProgramsModel(null); } else { return new TopProgramsModel(topProgramList); } } - + private static class ProgramNameCellValue { - + private final String programName; private final String programPath; - + public ProgramNameCellValue(String programName, String programPath) { this.programName = programName; this.programPath = programPath; } - + @Override public String toString() { return programName; } - + String getProgramName() { return programName; } - + String getProgramPath() { return programPath; } } - + private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { - + public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, @@ -158,7 +159,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { return c; } }; - + private static class TopProgramsModel extends AbstractTableModel { // column headers for artifact counts table @@ -168,34 +169,34 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header() }; - + private final List programResults; - + public TopProgramsModel(List programResults) { this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } - + @Override public String getColumnName(int column) { return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; } - + @Override public int getRowCount() { return programResults.size(); } - + @Override public int getColumnCount() { return TOP_PROGS_COLUMN_HEADERS.length; } - + @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex < 0 || rowIndex >= programResults.size()) { return null; } - + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); switch (columnIndex) { case 0: @@ -210,7 +211,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { return null; } } - + } /** From c57433644908f9dd33e0db0c7b3f7ca8875fc8cb Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 10 Aug 2020 07:48:04 -0400 Subject: [PATCH 12/15] fix to set scroll position --- .../datasourcesummary/DataSourceSummaryUserActivityPanel.form | 2 -- .../datasourcesummary/DataSourceSummaryUserActivityPanel.java | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index fe6637fac3..b938286e96 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -57,8 +57,6 @@ - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 63822ec4ea..681fa08617 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -100,6 +100,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER); topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80); topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150); + topProgramsScrollPane.getVerticalScrollBar().setValue(0); this.repaint(); } @@ -224,7 +225,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private void initComponents() { javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); - javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); + topProgramsScrollPane = new javax.swing.JScrollPane(); topProgramsTable = new javax.swing.JTable(); org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N @@ -256,6 +257,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane topProgramsScrollPane; private javax.swing.JTable topProgramsTable; // End of variables declaration//GEN-END:variables } From c870af5916243ac4a63e57d1f8e0b0406226aa35 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 10 Aug 2020 07:51:50 -0400 Subject: [PATCH 13/15] formatting --- .../DataSourceSummaryUserActivityPanel.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 681fa08617..9efbabe079 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -48,11 +48,11 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); private static final int TOP_PROGS_COUNT = 10; private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); - + static { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - + private DataSource dataSource; /** @@ -114,40 +114,40 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { List topProgramList = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - + if (topProgramList == null) { return new TopProgramsModel(null); } else { return new TopProgramsModel(topProgramList); } } - + private static class ProgramNameCellValue { - + private final String programName; private final String programPath; - + public ProgramNameCellValue(String programName, String programPath) { this.programName = programName; this.programPath = programPath; } - + @Override public String toString() { return programName; } - + String getProgramName() { return programName; } - + String getProgramPath() { return programPath; } } - + private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { - + public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, @@ -160,7 +160,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { return c; } }; - + private static class TopProgramsModel extends AbstractTableModel { // column headers for artifact counts table @@ -170,34 +170,34 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header() }; - + private final List programResults; - + public TopProgramsModel(List programResults) { this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } - + @Override public String getColumnName(int column) { return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; } - + @Override public int getRowCount() { return programResults.size(); } - + @Override public int getColumnCount() { return TOP_PROGS_COLUMN_HEADERS.length; } - + @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex < 0 || rowIndex >= programResults.size()) { return null; } - + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); switch (columnIndex) { case 0: @@ -212,7 +212,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { return null; } } - + } /** From 28fad44012a32053d73de70dce400bc7d52c82c9 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 10 Aug 2020 08:13:11 -0400 Subject: [PATCH 14/15] addressing codacy remarks --- .../DataSourceInfoUtilities.java | 6 ++-- .../DataSourceSummaryUserActivityPanel.java | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 85c827950c..7e66e56682 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -214,7 +214,7 @@ final class DataSourceInfoUtilities { * @return The result set handler to generate the map of string to long. */ private static ResultSetHandler> getStringLongResultSetHandler(String keyParam, String valueParam) { - ResultSetHandler> handler = (resultSet) -> { + return (resultSet) -> { LinkedHashMap toRet = new LinkedHashMap<>(); while (resultSet.next()) { try { @@ -226,8 +226,6 @@ final class DataSourceInfoUtilities { return toRet; }; - - return handler; } /** @@ -382,7 +380,7 @@ final class DataSourceInfoUtilities { * @return The generated 'where' statement. */ private static String getWhereString(List clauses) { - if (clauses.size() <= 0) { + if (clauses.isEmpty()) { return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 9efbabe079..c97e2ec18c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -24,6 +24,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; @@ -45,7 +46,8 @@ import org.sleuthkit.datamodel.DataSource; }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { - private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + private static final long serialVersionUID = 1L; + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); private static final int TOP_PROGS_COUNT = 10; private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); @@ -122,30 +124,45 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { } } + /** + * A POJO defining the values present in the name cell. Defines the name as + * well as the path for the tooltip. + */ private static class ProgramNameCellValue { private final String programName; private final String programPath; - public ProgramNameCellValue(String programName, String programPath) { + ProgramNameCellValue(String programName, String programPath) { this.programName = programName; this.programPath = programPath; } @Override public String toString() { + // override so that the value in the cell reads as programName return programName; } + /** + * @return The program name. + */ String getProgramName() { return programName; } + /** + * @return The path of the program. + */ String getProgramPath() { return programPath; } } + /** + * Defines a cell renderer for the first cell rendering the name as the text + * and path as the tooltip. + */ private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { public Component getTableCellRendererComponent( @@ -161,8 +178,14 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { } }; + /** + * Defines the table model for a JTable of the programs. Accepts a list of + * TopProgramsResult objects as rows data source. + */ private static class TopProgramsModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; + // column headers for artifact counts table private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), @@ -173,7 +196,12 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { private final List programResults; - public TopProgramsModel(List programResults) { + /** + * Main constructor. + * + * @param programResults The results to display. + */ + TopProgramsModel(List programResults) { this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } From c7d6c697ffb8eb85508e5fd501c53069063ff398 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 10 Aug 2020 15:27:41 -0400 Subject: [PATCH 15/15] revert bundle that was accidentally changed --- .../autopsy/casemodule/datasourcesummary/Bundle_ja.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties index a4e60c4f36..99fd24c7ef 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties @@ -71,4 +71,3 @@ DataSourceSummaryNode.column.tags.header=\u30bf\u30b0 DataSourceSummaryNode.column.type.header=\u30bf\u30a4\u30d7 DataSourceSummaryNode.viewDataSourceAction.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u79fb\u52d5 ViewSummaryInformationAction.name.text=\u30b5\u30de\u30ea\u30fc\u60c5\u5831\u3092\u8868\u793a -DataSourceSummaryUserActivityPanel.programsRunLabel.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c