From 45f064be9f5bf4f4b4f4e54027d37ca5a154e220 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 4 Mar 2021 14:07:14 -0500 Subject: [PATCH 01/34] starting custom domain categorization --- .../CustomDomainCategorizer.java | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java new file mode 100644 index 0000000000..7f49ec0318 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java @@ -0,0 +1,188 @@ +/* UNCLASSIFIED + * + * Viking + * + * Copyright (c) 2021 Basis Technology Corporation. + * Contact: brianc@basistech.com + */ +package org.sleuthkit.autopsy.recentactivity; + +import java.io.File; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.commons.lang3.StringUtils; +import org.openide.modules.InstalledFileLocator; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.url.analytics.DomainCategorizer; +import org.sleuthkit.autopsy.url.analytics.DomainCategorizerException; +import org.sleuthkit.autopsy.url.analytics.DomainCategory; + +/** + * A DomainCategoryProvider that utilizes a sqlite db for data. + */ +public class CustomDomainCategorizer implements DomainCategorizer { + + private static final String ROOT_FOLDER = "DomainCategorization"; + private static final String FILE_REL_PATH = "custom_categorization.db"; + + private static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; + private static final String SUFFIX_COLUMN = "suffix"; + private static final String CATEGORY_COLUMN = "category_id"; + + // get the suffix and category from the main table and gets the longest matching suffix. + private static final String BASE_QUERY_FMT_STR = String.format( + "SELECT %s, %s FROM domain_suffix WHERE suffix IN (%%s) ORDER BY LENGTH(%s) DESC LIMIT 1", + SUFFIX_COLUMN, CATEGORY_COLUMN, SUFFIX_COLUMN); + + private static final String CATEGORY_ID_COLUMN = "id"; + private static final String CATEGORY_NAME_COLUMN = "name"; + private static final String CATEGORY_IDS_QUERY = String.format("SELECT %s,%s FROM category", CATEGORY_ID_COLUMN, CATEGORY_NAME_COLUMN); + + private static final Logger logger = Logger.getLogger(CustomDomainCategorizer.class.getName()); + + private Connection dbConn = null; + private Map categoryIds = null; + + + /** + * Retrieves all the possible suffixes that could be tracked. For instance, + * if the host was "chatenabled.mail.google.com", the list should be + * ["chatenabled.mail.google.com", "mail.google.com", "google.com", "com"]. + * + * @param host The host. + * @return The possible suffixes. + */ + private List getSuffixes(String host) { + if (host == null) { + return null; + } + + List hostTokens = Arrays.asList(host.split("\\.")); + List hostSegmentations = new ArrayList<>(); + + for (int i = 0; i < hostTokens.size(); i++) { + String searchString = String.join(".", hostTokens.subList(i, hostTokens.size())); + hostSegmentations.add(searchString); + } + + return hostSegmentations; + } + + @Override + public DomainCategory getCategory(String domain, String host) throws DomainCategorizerException { + String hostToUse = (StringUtils.isBlank(host)) ? domain : host; + if (StringUtils.isBlank(hostToUse)) { + return null; + } + + hostToUse = hostToUse.toLowerCase(); + + List segmentations = getSuffixes(host); + String questionMarks = IntStream.range(0, segmentations.size()) + .mapToObj((num) -> "?") + .collect(Collectors.joining(",")); + + String sql = String.format(BASE_QUERY_FMT_STR, questionMarks); + + try (PreparedStatement stmt = dbConn.prepareStatement(sql)) { + for (int i = 0; i < segmentations.size(); i++) { + stmt.setString(i + 1, segmentations.get(i)); + } + + try (ResultSet resultSet = stmt.executeQuery()) { + if (resultSet.next()) { + String suffix = resultSet.getString(SUFFIX_COLUMN); + int categoryId = resultSet.getInt(CATEGORY_COLUMN); + String category = (resultSet.wasNull()) ? null : categoryIds.get(categoryId); + if (StringUtils.isNotBlank(suffix) && StringUtils.isNotBlank(category)) { + return new DomainCategory(suffix, category); + } + } + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "There was an error retrieving results for " + hostToUse, ex); + } + + return null; + } + + /** + * Retrieves a mapping of category ids to the name of the category. + * + * @param dbConn The database connection to the sqlite database with the + * category table. + * @return The mapping of category id to category name. + * @throws SQLException + */ + private Map getCategoryIds(Connection dbConn) throws SQLException { + if (dbConn == null) { + return null; + } + + Map toRet = new HashMap<>(); + try (PreparedStatement stmt = dbConn.prepareStatement(CATEGORY_IDS_QUERY)) { + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + toRet.put(resultSet.getInt(CATEGORY_ID_COLUMN), resultSet.getString(CATEGORY_NAME_COLUMN)); + } + } + } + + return toRet; + } + + @Override + public void initialize() throws DomainCategorizerException { + File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, CustomDomainCategorizer.class.getPackage().getName(), false); + + boolean needNew = true; + try { + if (dbConn != null && !dbConn.isClosed()) { + needNew = false; + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "There was an error while checking to see if previously existing connection.", ex); + } + + if (needNew) { + try { + if (dir == null || !dir.exists()) { + throw new DomainCategorizerException("Could not find parent directory of database"); + } + + File dbFile = Paths.get(dir.toString(), FILE_REL_PATH).toFile(); + if (dbFile == null || !dbFile.exists()) { + throw new DomainCategorizerException("Could not find database file in directory: " + dir.toString()); + } + + Class.forName("org.sqlite.JDBC"); + dbConn = DriverManager.getConnection(JDBC_SQLITE_PREFIX + dbFile.toString()); + categoryIds = getCategoryIds(dbConn); + } catch (ClassNotFoundException | SQLException ex) { + throw new DomainCategorizerException("Unable to connect to class resource db.", ex); + } + } + } + + @Override + public void close() throws Exception { + if (dbConn != null && !dbConn.isClosed()) { + dbConn.close(); + } + dbConn = null; + + categoryIds = null; + } +} From f851eb7a2636ac3a81f3431ad69dedd36b71f939 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 9 Mar 2021 13:52:52 -0500 Subject: [PATCH 02/34] new code --- .../url/analytics/AddEditCategoryDialog.form | 132 ++++++++++ .../url/analytics/AddEditCategoryDialog.java | 189 ++++++++++++++ .../url/analytics/Bundle.properties-MERGED | 4 + .../url/analytics/WebCategoriesManager.java | 238 ++++++++++++++++++ .../WebCategoriesOptionsController.java | 14 ++ .../analytics/WebCategoriesOptionsPanel.form | 176 +++++++++++++ .../analytics/WebCategoriesOptionsPanel.java | 201 +++++++++++++++ 7 files changed, 954 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsController.java create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form new file mode 100644 index 0000000000..5c2291c89e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form @@ -0,0 +1,132 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java new file mode 100644 index 0000000000..658b38396e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java @@ -0,0 +1,189 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.url.analytics; + +import org.openide.util.NbBundle.Messages; + +/** + * + * @author gregd + */ +public class AddEditCategoryDialog extends javax.swing.JDialog { + + private boolean changed = false; + + /** + * Creates new form AddEditCategoryDialog + */ + public AddEditCategoryDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + } + + /** + * @return The string value for the name in the input field if Ok pressed or + * null if not. + */ + DomainCategory getValue() { + return new DomainCategory(domainSuffixTextField.getText(), categoryTextField.getText()); + } + + /** + * @return Whether or not the value has been changed and the user pressed + * okay to save the new value. + */ + boolean isChanged() { + return changed; + } + + /** + * When the text field is updated, this method is called. + * + * @param newNameValue + */ + @Messages({ + "# {0} - maxSuffixLen", + "AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters.", + "# {0} - maxCategoryLen", + "AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a domain suffix that is no more than {0} characters.", + }) + void onValueUpdate(String suffixStr, String categoryStr) { + + String safeSuffixStr = suffixStr == null ? "" : suffixStr; + String safeCategoryStr = categoryStr == null ? "" : categoryStr; + + // update input text field if it is not the same. + if (!safeCategoryStr.equals(categoryTextField.getText())) { + categoryTextField.setText(safeCategoryStr); + } + + if (!safeSuffixStr.equals(domainSuffixTextField.getText())) { + domainSuffixTextField.setText(safeSuffixStr); + } + + if (safeSuffixStr.length() == 0 || safeSuffixStr.length() > WebCategoriesManager.getMaxDomainSuffixLength()) { + + } else if (safeCategoryStr.length() == 0 || safeCategoryStr.length() > WebCategoriesManager.getMaxCategoryLength()) { + + } + + // validate text input against invariants setting validation + // message and whether or not okay button is enabled accordingly. +// String validationMessage = HostNameValidator.getValidationMessage( +// newNameValue, initialHost == null ? null : initialHost.getName(), hostNamesSanitized); +// +// okButton.setEnabled(validationMessage == null); +// validationLabel.setText(validationMessage == null ? "" : validationMessage); + } + + + + /** + * 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() { + + categoryTextField = new javax.swing.JTextField(); + domainSuffixTextField = new javax.swing.JTextField(); + javax.swing.JLabel categoryLabel = new javax.swing.JLabel(); + javax.swing.JLabel domainSuffixLabel = new javax.swing.JLabel(); + validationLabel = new javax.swing.JLabel(); + javax.swing.JButton cancelButton = new javax.swing.JButton(); + saveButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + categoryLabel.setText("Category:"); + + domainSuffixLabel.setText("Domain Suffix:"); + + validationLabel.setForeground(java.awt.Color.RED); + validationLabel.setText(" "); + validationLabel.setToolTipText(""); + + cancelButton.setText("Cancel"); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + saveButton.setText("Save"); + saveButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + saveButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(validationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(domainSuffixLabel) + .addComponent(categoryLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(categoryTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE) + .addComponent(domainSuffixTextField))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 216, Short.MAX_VALUE) + .addComponent(saveButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(categoryLabel) + .addComponent(categoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(domainSuffixTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(domainSuffixLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(validationLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(saveButton) + .addComponent(cancelButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed + this.changed = true; + dispose(); + }//GEN-LAST:event_saveButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + this.changed = false; + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField categoryTextField; + private javax.swing.JTextField domainSuffixTextField; + private javax.swing.JButton saveButton; + private javax.swing.JLabel validationLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED new file mode 100644 index 0000000000..9c93495eb6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED @@ -0,0 +1,4 @@ +# {0} - maxCategoryLen +AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a domain suffix that is no more than {0} characters. +# {0} - maxSuffixLen +AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters. diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java new file mode 100644 index 0000000000..0236b92f0f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java @@ -0,0 +1,238 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.url.analytics; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.modules.InstalledFileLocator; + +/** + * + * @author gregd + */ +public class WebCategoriesManager { + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class CustomCategorizationJsonDto { + + private final String category; + private final List domains; + + @JsonCreator + public CustomCategorizationJsonDto( + @JsonProperty("category") String category, + @JsonProperty("domains") List domains) { + this.category = category; + this.domains = domains == null + ? Collections.emptyList() + : new ArrayList<>(domains); + } + + public String getCategory() { + return category; + } + + public List getDomains() { + return domains; + } + } + + + private static final int MAX_CAT_SIZE = 100; + private static final int MAX_DOMAIN_SIZE = 255; + + private static final String ROOT_FOLDER = "DomainCategorization"; + private static final String FILE_REL_PATH = "custom_list.db"; + private static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; + private static final Logger logger = Logger.getLogger(WebCategoriesManager.class.getName()); + + + private final File sqlitePath; + + + static int getMaxDomainSuffixLength() { + return MAX_DOMAIN_SIZE; + } + + static int getMaxCategoryLength() { + return MAX_DOMAIN_SIZE; + } + + + private static File getDefaultPath() { + File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, WebCategoriesManager.class.getPackage().getName(), false); + return Paths.get(dir.getAbsolutePath(), FILE_REL_PATH).toFile(); + } + + public WebCategoriesManager(File sqlitePath) { + this.sqlitePath = sqlitePath; + } + + public WebCategoriesManager() { + this(getDefaultPath()); + } + + + + private Connection getConnection() throws SQLException { + String url = JDBC_SQLITE_PREFIX + sqlitePath.getAbsolutePath(); + return DriverManager.getConnection(url); + } + + public void convertJsonToSqlite(File jsonInput) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + List customCategorizations = mapper.readValue(jsonInput, new TypeReference>() { + }); + + customCategorizations = customCategorizations == null ? Collections.emptyList() : customCategorizations; + + + try (Connection conn = getConnection()) { + if (conn != null) { + try (Statement turnOffWal = conn.createStatement()) { + turnOffWal.execute("PRAGMA journal_mode=OFF"); + } + + try (Statement createDomainsTable = conn.createStatement()) { + createDomainsTable.execute( + " CREATE TABLE domain_suffix (\n" + + " suffix VARCHAR(" + MAX_DOMAIN_SIZE + ") PRIMARY KEY,\n" + + " category VARCHAR(" + MAX_CAT_SIZE + ")\n" + + " ) WITHOUT ROWID"); + } + + try (PreparedStatement domainInsert = conn.prepareStatement( + "INSERT INTO domain_suffix(suffix, category) VALUES (?, ?)", Statement.NO_GENERATED_KEYS)) { + + for (int i = 0; i < customCategorizations.size(); i++) { + CustomCategorizationJsonDto category = customCategorizations.get(i); + if (category == null || category.getDomains() == null || category.getCategory() == null) { + logger.log(Level.WARNING, String.format("Could not process item in file: %s at index: %d", jsonInput.getAbsolutePath(), i)); + continue; + } + + String categoryStr = category.getCategory().substring(0, Math.max(category.getCategory().length(), MAX_CAT_SIZE)); + + for (int listIdx = 0; listIdx < category.getDomains().size(); i++) { + String domain = category.getDomains().get(listIdx); + if (domain == null) { + logger.log(Level.WARNING, String.format("Could not process domain at idx: %d in category %s for file %s", + listIdx, categoryStr, jsonInput.getAbsolutePath())); + } + + domainInsert.setString(1, domain); + domainInsert.setString(2, categoryStr); + domainInsert.addBatch(); + } + } + + domainInsert.executeBatch(); + } catch (SQLException ex) { + logger.log(Level.WARNING, "There was an error while writing json to sqlite", ex); + } + } + + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } + + public void convertSqliteToJson(File jsonOutput) throws SQLException, IOException { + List> categoryDomains = new ArrayList<>(); + try (Connection conn = getConnection(); + Statement domainSelect = conn.createStatement(); + ResultSet resultSet = domainSelect.executeQuery( + "SELECT suffix, category FROM domain_suffix")) { + + while (resultSet.next()) { + categoryDomains.add(Pair.of(resultSet.getString("category"), resultSet.getString("domain"))); + } + } + + List categories + = categoryDomains.stream() + .collect(Collectors.toMap( + p -> p.getKey(), + p -> new ArrayList<>(Arrays.asList(p.getValue())), + (p1, p2) -> { + p1.addAll(p2); + return p1; + } + )) + .entrySet().stream() + .map(entry -> new CustomCategorizationJsonDto(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(jsonOutput, categories); + } + + public boolean deleteRecord(String domainSuffix) throws SQLException { + try (Connection conn = getConnection()) { + if (domainSuffix == null || domainSuffix.length() > MAX_DOMAIN_SIZE) { + throw new IllegalArgumentException("Expected non-empty category <= " + MAX_CAT_SIZE + " characters and non-empty domain suffix <= " + MAX_DOMAIN_SIZE + " characters."); + } + + try (PreparedStatement suffixDelete = conn.prepareStatement("DELETE FROM domain_suffix WHERE LOWER(suffix) = LOWER(?)", Statement.RETURN_GENERATED_KEYS);) { + + suffixDelete.setString(1, domainSuffix.trim()); + return suffixDelete.executeUpdate() > 0; + } + } + } + + public boolean insertUpdateSuffix(DomainCategory entry) throws SQLException, IllegalStateException, IllegalArgumentException { + try (Connection conn = getConnection()) { + if (entry == null + || StringUtils.isBlank(entry.getCategory()) || (entry.getCategory().length() > MAX_CAT_SIZE) + || StringUtils.isBlank(entry.getHostSuffix()) || (entry.getHostSuffix().length() > MAX_DOMAIN_SIZE)) { + throw new IllegalArgumentException("Expected non-empty category <= 100 characters and non-empty domain suffix <= 255 characters."); + } + + try (PreparedStatement insertUpdate = conn.prepareStatement("INSERT OR REPLACE INTO domain_suffix(suffix, category) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);) { + insertUpdate.setString(1, entry.getHostSuffix().trim()); + insertUpdate.setString(1, entry.getCategory().trim()); + return insertUpdate.executeUpdate() > 0; + } + } + } + + public List getRecords() throws SQLException { + try (Connection conn = getConnection()) { + List entries = new ArrayList<>(); + try (Statement domainSelect = conn.createStatement(); + ResultSet resultSet = domainSelect.executeQuery("SELECT suffix, category FROM domain_suffix")) { + while (resultSet.next()) { + entries.add(new DomainCategory( + resultSet.getString("suffix"), + resultSet.getString("category"))); + } + } + return entries; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsController.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsController.java new file mode 100644 index 0000000000..96f79806f5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsController.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.url.analytics; + +/** + * + * @author gregd + */ +public class WebCategoriesOptionsController { + +} diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form new file mode 100644 index 0000000000..a6953dcd15 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form @@ -0,0 +1,176 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java new file mode 100644 index 0000000000..2d9949934d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java @@ -0,0 +1,201 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.url.analytics; + +import org.sleuthkit.autopsy.corecomponents.OptionsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; + +/** + * + * @author gregd + */ +public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { + + /** + * Creates new form CustomWebCategoriesOptionsPanel + */ + public WebCategoriesOptionsPanel() { + initComponents(); + } + + /** + * 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() { + java.awt.GridBagConstraints gridBagConstraints; + + javax.swing.JLabel panelDescription = new javax.swing.JLabel(); + javax.swing.JLabel categoriesTitle = new javax.swing.JLabel(); + javax.swing.JScrollPane categoriesScrollPane = new javax.swing.JScrollPane(); + categoriesTable = new javax.swing.JTable(); + javax.swing.JButton newEntryButton = new javax.swing.JButton(); + editEntryButton = new javax.swing.JButton(); + deleteEntryButton = new javax.swing.JButton(); + javax.swing.JButton importSetButton = new javax.swing.JButton(); + javax.swing.JButton exportSetButton = new javax.swing.JButton(); + javax.swing.JPanel bottomStrut = new javax.swing.JPanel(); + + setLayout(new java.awt.GridBagLayout()); + + panelDescription.setText("This module allows you to find web categories based on host name."); + panelDescription.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(10, 10, 10, 0); + add(panelDescription, gridBagConstraints); + + categoriesTitle.setText("Categories:"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 0); + add(categoriesTitle, gridBagConstraints); + + categoriesScrollPane.setMinimumSize(new java.awt.Dimension(250, 350)); + + categoriesTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + {null, null, null, null}, + {null, null, null, null}, + {null, null, null, null}, + {null, null, null, null} + }, + new String [] { + "Title 1", "Title 2", "Title 3", "Title 4" + } + )); + categoriesScrollPane.setViewportView(categoriesTable); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 0); + add(categoriesScrollPane, gridBagConstraints); + + newEntryButton.setText("New Entry"); + newEntryButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newEntryButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); + add(newEntryButton, gridBagConstraints); + + editEntryButton.setText("Edit Entry"); + editEntryButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + editEntryButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); + add(editEntryButton, gridBagConstraints); + + deleteEntryButton.setText("Delete Entry"); + deleteEntryButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteEntryButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); + add(deleteEntryButton, gridBagConstraints); + + importSetButton.setText("Import Set"); + importSetButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + importSetButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); + add(importSetButton, gridBagConstraints); + + exportSetButton.setText("Export Set"); + exportSetButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exportSetButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 4; + gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); + add(exportSetButton, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 5; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(bottomStrut, gridBagConstraints); + }// //GEN-END:initComponents + + private void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_deleteEntryButtonActionPerformed + + private void newEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newEntryButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_newEntryButtonActionPerformed + + private void editEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editEntryButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_editEntryButtonActionPerformed + + private void importSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importSetButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_importSetButtonActionPerformed + + private void exportSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportSetButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_exportSetButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTable categoriesTable; + private javax.swing.JButton deleteEntryButton; + private javax.swing.JButton editEntryButton; + // End of variables declaration//GEN-END:variables + + @Override + public void saveSettings() { + + } + + @Override + public void store() { + + } + + @Override + public void load() { + + } +} From 7695ef6a90e4a8d6a4e0108d1a3d95a0f1dce735 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 9 Mar 2021 15:29:19 -0500 Subject: [PATCH 03/34] bundles updates --- Core/build.xml | 2 + .../url/analytics/AddEditCategoryDialog.form | 16 +++-- .../url/analytics/AddEditCategoryDialog.java | 56 +++++++++------ .../autopsy/url/analytics/Bundle.properties | 6 ++ .../url/analytics/Bundle.properties-MERGED | 8 +++ ...nager.java => WebCategoriesDataModel.java} | 10 +-- .../analytics/WebCategoriesOptionsPanel.form | 4 +- .../analytics/WebCategoriesOptionsPanel.java | 30 ++++++-- .../WebCategoryOptionsController.java | 72 +++++++++++++++++++ 9 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties rename Core/src/org/sleuthkit/autopsy/url/analytics/{WebCategoriesManager.java => WebCategoriesDataModel.java} (97%) create mode 100644 Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoryOptionsController.java diff --git a/Core/build.xml b/Core/build.xml index e8966c2508..31379f9772 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -89,6 +89,8 @@ + + diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form index 5c2291c89e..4e51979df7 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.form @@ -83,7 +83,9 @@ - + + + @@ -92,7 +94,9 @@ - + + + @@ -110,7 +114,9 @@ - + + + @@ -122,7 +128,9 @@ - + + + diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java index 658b38396e..771b826577 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/AddEditCategoryDialog.java @@ -5,6 +5,7 @@ */ package org.sleuthkit.autopsy.url.analytics; +import java.util.Set; import org.openide.util.NbBundle.Messages; /** @@ -14,21 +15,31 @@ import org.openide.util.NbBundle.Messages; public class AddEditCategoryDialog extends javax.swing.JDialog { private boolean changed = false; + private final Set currentSuffixesToUpper; + private final String currentSuffix; + private final String currentCategory; /** * Creates new form AddEditCategoryDialog */ - public AddEditCategoryDialog(java.awt.Frame parent, boolean modal) { - super(parent, modal); - initComponents(); + public AddEditCategoryDialog(java.awt.Frame parent, Set currentSuffixesToUpper) { + this(parent, currentSuffixesToUpper, null, null); } + public AddEditCategoryDialog(java.awt.Frame parent, Set currentSuffixesToUpper, String currentSuffix, String currentCategory) { + super(parent, true); + initComponents(); + this.currentSuffixesToUpper = currentSuffixesToUpper; + this.currentSuffix = currentSuffix; + this.currentCategory = currentCategory; + } + /** * @return The string value for the name in the input field if Ok pressed or * null if not. */ DomainCategory getValue() { - return new DomainCategory(domainSuffixTextField.getText(), categoryTextField.getText()); + return new DomainCategory(domainSuffixTextField.getText().trim(), categoryTextField.getText().trim()); } /** @@ -49,6 +60,8 @@ public class AddEditCategoryDialog extends javax.swing.JDialog { "AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters.", "# {0} - maxCategoryLen", "AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a domain suffix that is no more than {0} characters.", + "AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix.", + "AddEditCategoryDialog_onValueUpdate_sameCategory=Please provide a new category for this domain suffix.", }) void onValueUpdate(String suffixStr, String categoryStr) { @@ -64,22 +77,23 @@ public class AddEditCategoryDialog extends javax.swing.JDialog { domainSuffixTextField.setText(safeSuffixStr); } - if (safeSuffixStr.length() == 0 || safeSuffixStr.length() > WebCategoriesManager.getMaxDomainSuffixLength()) { - - } else if (safeCategoryStr.length() == 0 || safeCategoryStr.length() > WebCategoriesManager.getMaxCategoryLength()) { - - } - - // validate text input against invariants setting validation - // message and whether or not okay button is enabled accordingly. -// String validationMessage = HostNameValidator.getValidationMessage( -// newNameValue, initialHost == null ? null : initialHost.getName(), hostNamesSanitized); -// -// okButton.setEnabled(validationMessage == null); -// validationLabel.setText(validationMessage == null ? "" : validationMessage); + String validationMessage = null; + if (safeSuffixStr.trim().length() == 0 || safeSuffixStr.trim().length() > WebCategoriesDataModel.getMaxDomainSuffixLength()) { + validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badSuffix(WebCategoriesDataModel.getMaxCategoryLength()); + }else if (safeCategoryStr.trim().length() == 0 || safeCategoryStr.trim().length() > WebCategoriesDataModel.getMaxCategoryLength()) { + validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badCategory(WebCategoriesDataModel.getMaxDomainSuffixLength()); + } else if (currentSuffixesToUpper.contains(safeSuffixStr.trim().toUpperCase())) { + validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_suffixRepeat(); + } else if (currentCategory != null && safeCategoryStr.trim().equals(currentCategory.trim())) { + validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_sameCategory(); + } + + saveButton.setEnabled(validationMessage == null); + validationLabel.setText(validationMessage == null ? "" : validationMessage); } + /** * This method is called from within the constructor to initialize the form. @@ -100,22 +114,22 @@ public class AddEditCategoryDialog extends javax.swing.JDialog { setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - categoryLabel.setText("Category:"); + categoryLabel.setText(org.openide.util.NbBundle.getMessage(AddEditCategoryDialog.class, "AddEditCategoryDialog.categoryLabel.text")); // NOI18N - domainSuffixLabel.setText("Domain Suffix:"); + domainSuffixLabel.setText(org.openide.util.NbBundle.getMessage(AddEditCategoryDialog.class, "AddEditCategoryDialog.domainSuffixLabel.text")); // NOI18N validationLabel.setForeground(java.awt.Color.RED); validationLabel.setText(" "); validationLabel.setToolTipText(""); - cancelButton.setText("Cancel"); + cancelButton.setText(org.openide.util.NbBundle.getMessage(AddEditCategoryDialog.class, "AddEditCategoryDialog.cancelButton.text")); // NOI18N cancelButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelButtonActionPerformed(evt); } }); - saveButton.setText("Save"); + saveButton.setText(org.openide.util.NbBundle.getMessage(AddEditCategoryDialog.class, "AddEditCategoryDialog.saveButton.text")); // NOI18N saveButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { saveButtonActionPerformed(evt); diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties new file mode 100644 index 0000000000..58a1d65fbb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties @@ -0,0 +1,6 @@ +WebCategoryOptionsController_title=Custom Web Categories +WebCategoryOptionsController_keywords=Custom Web Categories +AddEditCategoryDialog.categoryLabel.text=Category: +AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: +AddEditCategoryDialog.saveButton.text=Save +AddEditCategoryDialog.cancelButton.text=Cancel \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED index 9c93495eb6..655b63831b 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/Bundle.properties-MERGED @@ -2,3 +2,11 @@ AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a domain suffix that is no more than {0} characters. # {0} - maxSuffixLen AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters. +AddEditCategoryDialog_onValueUpdate_sameCategory=Please provide a new category for this domain suffix. +AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix. +WebCategoryOptionsController_title=Custom Web Categories +WebCategoryOptionsController_keywords=Custom Web Categories +AddEditCategoryDialog.categoryLabel.text=Category: +AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: +AddEditCategoryDialog.saveButton.text=Save +AddEditCategoryDialog.cancelButton.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesDataModel.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java rename to Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesDataModel.java index 0236b92f0f..cf03243984 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesManager.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesDataModel.java @@ -34,7 +34,7 @@ import org.openide.modules.InstalledFileLocator; * * @author gregd */ -public class WebCategoriesManager { +public class WebCategoriesDataModel { @JsonIgnoreProperties(ignoreUnknown = true) public static class CustomCategorizationJsonDto { @@ -68,7 +68,7 @@ public class WebCategoriesManager { private static final String ROOT_FOLDER = "DomainCategorization"; private static final String FILE_REL_PATH = "custom_list.db"; private static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; - private static final Logger logger = Logger.getLogger(WebCategoriesManager.class.getName()); + private static final Logger logger = Logger.getLogger(WebCategoriesDataModel.class.getName()); private final File sqlitePath; @@ -84,15 +84,15 @@ public class WebCategoriesManager { private static File getDefaultPath() { - File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, WebCategoriesManager.class.getPackage().getName(), false); + File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, WebCategoriesDataModel.class.getPackage().getName(), false); return Paths.get(dir.getAbsolutePath(), FILE_REL_PATH).toFile(); } - public WebCategoriesManager(File sqlitePath) { + public WebCategoriesDataModel(File sqlitePath) { this.sqlitePath = sqlitePath; } - public WebCategoriesManager() { + public WebCategoriesDataModel() { this(getDefaultPath()); } diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form index a6953dcd15..53d451c9fb 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.form @@ -18,7 +18,9 @@ - + + + diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java index 2d9949934d..dbe19cd16a 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoriesOptionsPanel.java @@ -5,6 +5,9 @@ */ package org.sleuthkit.autopsy.url.analytics; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; @@ -14,11 +17,23 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; */ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { + private static final Logger logger = Logger.getLogger(WebCategoriesOptionsPanel.class.getName()); + private final WebCategoriesDataModel dataModel; + /** * Creates new form CustomWebCategoriesOptionsPanel */ - public WebCategoriesOptionsPanel() { + public WebCategoriesOptionsPanel(WebCategoriesDataModel dataModel) { initComponents(); + this.dataModel = dataModel; + } + + private DomainCategory getSelected() { + return null; + } + + void refresh() { + } /** @@ -44,7 +59,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i setLayout(new java.awt.GridBagLayout()); - panelDescription.setText("This module allows you to find web categories based on host name."); + panelDescription.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.panelDescription.text")); // NOI18N panelDescription.setBorder(javax.swing.BorderFactory.createEtchedBorder()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridwidth = 3; @@ -158,7 +173,14 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i }// //GEN-END:initComponents private void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryButtonActionPerformed - // TODO add your handling code here: + DomainCategory selected = getSelected(); + if (selected != null && selected.getHostSuffix() != null) { + try { + dataModel.deleteRecord(selected.getHostSuffix()); + } catch (SQLException ex) { + logger.log(Level.WARNING, "There was an error while deleting: " + selected.getHostSuffix(), ex); + } + } }//GEN-LAST:event_deleteEntryButtonActionPerformed private void newEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newEntryButtonActionPerformed @@ -196,6 +218,6 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i @Override public void load() { - + refresh(); } } diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoryOptionsController.java b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoryOptionsController.java new file mode 100644 index 0000000000..d543097623 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/WebCategoryOptionsController.java @@ -0,0 +1,72 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.url.analytics; + +import java.beans.PropertyChangeListener; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +/** + * + * @author gregd + */ +@OptionsPanelController.TopLevelRegistration(categoryName = "#WebCategoryOptionsController_title", + iconBase = "org/sleuthkit/autopsy/corecomponents/checkbox32.png", + position = 21, + keywords = "#WebCategoryOptionsController_keywords", + keywordsCategory = "Custom Web Categories") +public class WebCategoryOptionsController extends OptionsPanelController { + private final WebCategoriesDataModel dataModel = new WebCategoriesDataModel(); + private final WebCategoriesOptionsPanel panel = new WebCategoriesOptionsPanel(dataModel); + + @Override + public void update() { + panel.refresh(); + } + + @Override + public void applyChanges() { + // NO OP since saves happen whenever there is a change. + } + + @Override + public void cancel() { + // NO OP since saves happen whenever there is a change. + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean isChanged() { + return false; + } + + @Override + public JComponent getComponent(Lookup masterLookup) { + return panel; + } + + @Override + public HelpCtx getHelpCtx() { + return null; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + // NO OP since saves happen whenever there is a change. + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + // NO OP since saves happen whenever there is a change. + } + +} From 24a248ae7382eb9b84f6d172f8303efbd0ea664c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 9 Mar 2021 15:57:36 -0500 Subject: [PATCH 04/34] updated build for DomainCategorization --- Core/build.xml | 5 ++- .../url/analytics/AddEditCategoryDialog.form | 6 +++ .../url/analytics/AddEditCategoryDialog.java | 26 +++++++++++++ .../autopsy/url/analytics/Bundle.properties | 9 ++++- .../url/analytics/Bundle.properties-MERGED | 7 ++++ .../analytics/WebCategoriesOptionsPanel.form | 37 ++++++++++++++----- .../analytics/WebCategoriesOptionsPanel.java | 14 +++---- thirdparty/DomainCategorization/README.txt | 1 + 8 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 thirdparty/DomainCategorization/README.txt diff --git a/Core/build.xml b/Core/build.xml index 31379f9772..40b8e966a3 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -89,7 +89,10 @@ - + + + +