fixes and changes

This commit is contained in:
Greg DiCristofaro 2021-03-11 14:33:35 -05:00
parent 2062ab5113
commit 0415a4eef1
10 changed files with 104 additions and 243 deletions

View File

@ -353,15 +353,16 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
} }
/** /**
* Returns the selected item or null if no item is selected. * Returns the selected items or null if no item is selected.
* @return The selected item or null if no item is selected. * @return The selected items or null if no item is selected.
*/ */
public T getSelectedItem() { public List<T> getSelectedItems() {
int selectedRow = this.table.getSelectedRow(); int selectedRow = this.table.getSelectedRow();
if (selectedRow < 0 || this.tableModel == null || selectedRow >= this.tableModel.getDataRows().size()) { int count = this.table.getSelectedRowCount();
if (selectedRow < 0 || this.tableModel == null || selectedRow + count > this.tableModel.getDataRows().size()) {
return null; return null;
} else { } else {
return this.tableModel.getDataRows().get(selectedRow); return this.tableModel.getDataRows().subList(selectedRow, selectedRow + count);
} }
} }

View File

@ -63,13 +63,13 @@
<Component id="categoryLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="categoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="validationLabel" min="-2" pref="34" max="-2" attributes="0"/> <Component id="validationLabel" min="-2" pref="46" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="saveButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="saveButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace pref="12" max="32767" attributes="0"/> <EmptySpace pref="8" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -54,6 +54,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
onValueUpdate(domainSuffixTextField.getText(), categoryTextField.getText()); onValueUpdate(domainSuffixTextField.getText(), categoryTextField.getText());
} }
}; };
/** /**
* Main constructor if adding a new domain suffix. * Main constructor if adding a new domain suffix.
* *
@ -77,8 +78,6 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
initComponents(); initComponents();
this.currentSuffixes = currentSuffixes; this.currentSuffixes = currentSuffixes;
this.currentDomainCategory = currentDomainCategory; this.currentDomainCategory = currentDomainCategory;
// set title based on whether or not we are editing or adding // set title based on whether or not we are editing or adding
// also don't allow editing of domain suffix if editing // also don't allow editing of domain suffix if editing
@ -93,9 +92,9 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
domainSuffixTextField.setEnabled(false); domainSuffixTextField.setEnabled(false);
onValueUpdate(currentDomainCategory.getHostSuffix(), currentDomainCategory.getCategory()); onValueUpdate(currentDomainCategory.getHostSuffix(), currentDomainCategory.getCategory());
} }
validationLabel.setText(""); validationLabel.setText("");
categoryTextField.getDocument().addDocumentListener(updateListener); categoryTextField.getDocument().addDocumentListener(updateListener);
domainSuffixTextField.getDocument().addDocumentListener(updateListener); domainSuffixTextField.getDocument().addDocumentListener(updateListener);
} }
@ -103,6 +102,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
/** /**
* Returns the string value for the name in the input field if Ok pressed or * Returns the string value for the name in the input field if Ok pressed or
* null if not. * null if not.
*
* @return The string value for the name in the input field if Ok pressed or * @return The string value for the name in the input field if Ok pressed or
* null if not. * null if not.
*/ */
@ -112,6 +112,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
/** /**
* Returns whether or not the value has been changed and saved by the user. * Returns whether or not the value has been changed and saved by the user.
*
* @return Whether or not the value has been changed and saved by the user. * @return Whether or not the value has been changed and saved by the user.
*/ */
boolean isChanged() { boolean isChanged() {
@ -126,7 +127,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
*/ */
@Messages({ @Messages({
"# {0} - maxSuffixLen", "# {0} - maxSuffixLen",
"AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters.", "AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters that includes at least one period.",
"# {0} - maxCategoryLen", "# {0} - maxCategoryLen",
"AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters.", "AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters.",
"AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix.", "AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix.",
@ -147,16 +148,18 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
} }
String validationMessage = null; String validationMessage = null;
if (safeSuffixStr.trim().length() == 0 || safeSuffixStr.trim().length() > WebCategoriesDataModel.getMaxDomainSuffixLength()) { if (safeSuffixStr.trim().length() == 0
|| safeSuffixStr.trim().length() > WebCategoriesDataModel.getMaxDomainSuffixLength()
|| safeSuffixStr.indexOf('.') < 0) {
validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badSuffix(WebCategoriesDataModel.getMaxCategoryLength()); validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badSuffix(WebCategoriesDataModel.getMaxCategoryLength());
} else if (safeCategoryStr.trim().length() == 0 || safeCategoryStr.trim().length() > WebCategoriesDataModel.getMaxCategoryLength()) { } else if (safeCategoryStr.trim().length() == 0 || safeCategoryStr.trim().length() > WebCategoriesDataModel.getMaxCategoryLength()) {
validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badCategory(WebCategoriesDataModel.getMaxCategoryLength()); validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badCategory(WebCategoriesDataModel.getMaxCategoryLength());
} else if (currentSuffixes.contains(normalizedSuffix) && } else if (currentSuffixes.contains(normalizedSuffix)
(currentDomainCategory == null || && (currentDomainCategory == null
!normalizedSuffix.equals(currentDomainCategory.getHostSuffix()))) { || !normalizedSuffix.equals(currentDomainCategory.getHostSuffix()))) {
validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_suffixRepeat(); validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_suffixRepeat();
} else if (currentDomainCategory != null } else if (currentDomainCategory != null
@ -167,7 +170,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
} }
saveButton.setEnabled(validationMessage == null); saveButton.setEnabled(validationMessage == null);
validationLabel.setText(validationMessage == null ? "" : validationMessage); validationLabel.setText(validationMessage == null ? "" : String.format("<html>%s</html>", validationMessage));
} }
/** /**
@ -246,12 +249,12 @@ class AddEditCategoryDialog extends javax.swing.JDialog {
.addComponent(categoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(categoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(categoryLabel)) .addComponent(categoryLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(validationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(validationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(saveButton) .addComponent(cancelButton)
.addComponent(cancelButton)) .addComponent(saveButton))
.addContainerGap(12, Short.MAX_VALUE)) .addContainerGap(8, Short.MAX_VALUE))
); );
pack(); pack();

View File

@ -4,7 +4,7 @@ AddEditCategoryDialog.categoryLabel.text=Category:
AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix:
AddEditCategoryDialog.saveButton.text=Save AddEditCategoryDialog.saveButton.text=Save
AddEditCategoryDialog.cancelButton.text=Cancel AddEditCategoryDialog.cancelButton.text=Cancel
WebCategoriesOptionsPanel.panelDescription.text=This module allows you to find web categories based on host name. WebCategoriesOptionsPanel.panelDescription.text=This module allows you to classify web sites based on domain names.
WebCategoriesOptionsPanel.categoriesTitle.text=Categories: WebCategoriesOptionsPanel.categoriesTitle.text=Categories:
WebCategoriesOptionsPanel.newEntryButton.text=New Entry WebCategoriesOptionsPanel.newEntryButton.text=New Entry
WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry

View File

@ -3,7 +3,7 @@ AddEditCategoryDialog_Edit=Edit Entry
# {0} - maxCategoryLen # {0} - maxCategoryLen
AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters. AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters.
# {0} - maxSuffixLen # {0} - maxSuffixLen
AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters. AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters that includes at least one period.
AddEditCategoryDialog_onValueUpdate_sameCategory=Please provide a new category for this domain suffix. AddEditCategoryDialog_onValueUpdate_sameCategory=Please provide a new category for this domain suffix.
AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix. AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix.
WebCategoriesOptionsPanel_categoryTable_categoryColumnName=Category WebCategoriesOptionsPanel_categoryTable_categoryColumnName=Category
@ -19,7 +19,7 @@ AddEditCategoryDialog.categoryLabel.text=Category:
AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix:
AddEditCategoryDialog.saveButton.text=Save AddEditCategoryDialog.saveButton.text=Save
AddEditCategoryDialog.cancelButton.text=Cancel AddEditCategoryDialog.cancelButton.text=Cancel
WebCategoriesOptionsPanel.panelDescription.text=This module allows you to find web categories based on host name. WebCategoriesOptionsPanel.panelDescription.text=This module allows you to classify web sites based on domain names.
WebCategoriesOptionsPanel.categoriesTitle.text=Categories: WebCategoriesOptionsPanel.categoriesTitle.text=Categories:
WebCategoriesOptionsPanel.newEntryButton.text=New Entry WebCategoriesOptionsPanel.newEntryButton.text=New Entry
WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry

View File

@ -28,7 +28,9 @@ import org.sleuthkit.autopsy.url.analytics.DomainCategorizerException;
import org.sleuthkit.autopsy.url.analytics.DomainCategory; import org.sleuthkit.autopsy.url.analytics.DomainCategory;
/** /**
* A DomainCategoryProvider for custom web categories. * A DomainCategoryProvider for custom web categories. NOTE: If this class
* package or name change, code in DomainCategoryRunner will also need to change
* to reflect the changing class name for ordering purposes.
*/ */
@ServiceProvider(service = DomainCategorizer.class) @ServiceProvider(service = DomainCategorizer.class)
public class CustomWebCategorizer implements DomainCategorizer { public class CustomWebCategorizer implements DomainCategorizer {

View File

@ -89,6 +89,9 @@
</Container> </Container>
<Component class="javax.swing.JButton" name="newEntryButton"> <Component class="javax.swing.JButton" name="newEntryButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.newEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.newEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -104,6 +107,9 @@
</Component> </Component>
<Component class="javax.swing.JButton" name="editEntryButton"> <Component class="javax.swing.JButton" name="editEntryButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/edit16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.editEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.editEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -119,6 +125,9 @@
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteEntryButton"> <Component class="javax.swing.JButton" name="deleteEntryButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/delete16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.deleteEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.deleteEntryButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -134,6 +143,9 @@
</Component> </Component>
<Component class="javax.swing.JButton" name="importSetButton"> <Component class="javax.swing.JButton" name="importSetButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/import16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.importSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.importSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -149,6 +161,9 @@
</Component> </Component>
<Component class="javax.swing.JButton" name="exportSetButton"> <Component class="javax.swing.JButton" name="exportSetButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/export16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.exportSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties" key="WebCategoriesOptionsPanel.exportSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>

View File

@ -35,6 +35,7 @@ import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.collections.CollectionUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.WeakListeners; import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
@ -101,12 +102,12 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
} }
/** /**
* Returns the item selected in the table or null if no selection. * Returns the items selected in the table or null if no selection.
* *
* @return The item selected in the table or null if no selection. * @return The items selected in the table or null if no selection.
*/ */
private DomainCategory getSelected() { private List<DomainCategory> getSelected() {
return categoryTable.getSelectedItem(); return categoryTable.getSelectedItems();
} }
/** /**
@ -147,12 +148,13 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
* happening. * happening.
*/ */
private void refreshComponentStates() { private void refreshComponentStates() {
boolean selectedItem = getSelected() != null; List<DomainCategory> selectedItems = getSelected();
int selectedCount = CollectionUtils.isEmpty(selectedItems) ? 0 : selectedItems.size();
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
boolean operationsPermitted = !isIngestRunning && !isRefreshing; boolean operationsPermitted = !isIngestRunning && !isRefreshing;
deleteEntryButton.setEnabled(selectedItem && operationsPermitted); deleteEntryButton.setEnabled(selectedCount > 0 && operationsPermitted);
editEntryButton.setEnabled(selectedItem && operationsPermitted); editEntryButton.setEnabled(selectedCount == 1 && operationsPermitted);
newEntryButton.setEnabled(operationsPermitted); newEntryButton.setEnabled(operationsPermitted);
exportSetButton.setEnabled(operationsPermitted); exportSetButton.setEnabled(operationsPermitted);
@ -287,6 +289,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
gridBagConstraints.insets = new java.awt.Insets(2, 10, 10, 0); gridBagConstraints.insets = new java.awt.Insets(2, 10, 10, 0);
add(categoryTablePanel, gridBagConstraints); add(categoryTablePanel, gridBagConstraints);
newEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
newEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.newEntryButton.text")); // NOI18N newEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.newEntryButton.text")); // NOI18N
newEntryButton.addActionListener(new java.awt.event.ActionListener() { newEntryButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -301,6 +304,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5);
add(newEntryButton, gridBagConstraints); add(newEntryButton, gridBagConstraints);
editEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N
editEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.editEntryButton.text")); // NOI18N editEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.editEntryButton.text")); // NOI18N
editEntryButton.addActionListener(new java.awt.event.ActionListener() { editEntryButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -315,6 +319,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(editEntryButton, gridBagConstraints); add(editEntryButton, gridBagConstraints);
deleteEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N
deleteEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.deleteEntryButton.text")); // NOI18N deleteEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.deleteEntryButton.text")); // NOI18N
deleteEntryButton.addActionListener(new java.awt.event.ActionListener() { deleteEntryButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -328,6 +333,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5);
add(deleteEntryButton, gridBagConstraints); add(deleteEntryButton, gridBagConstraints);
importSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/import16.png"))); // NOI18N
importSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.importSetButton.text")); // NOI18N importSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.importSetButton.text")); // NOI18N
importSetButton.addActionListener(new java.awt.event.ActionListener() { importSetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -342,6 +348,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5);
add(importSetButton, gridBagConstraints); add(importSetButton, gridBagConstraints);
exportSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/export16.png"))); // NOI18N
exportSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.exportSetButton.text")); // NOI18N exportSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.exportSetButton.text")); // NOI18N
exportSetButton.addActionListener(new java.awt.event.ActionListener() { exportSetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -375,14 +382,20 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryButtonActionPerformed private void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryButtonActionPerformed
DomainCategory selected = getSelected(); List<DomainCategory> selectedItems = getSelected();
if (selected != null && selected.getHostSuffix() != null) { if (!CollectionUtils.isEmpty(selectedItems)) {
try { setWaitingCursor();
runUpdateAction(() -> dataModel.deleteRecord(selected.getHostSuffix())); for (DomainCategory selected : selectedItems) {
} catch (IllegalArgumentException | SQLException | IOException ex) { if (selected != null && selected.getHostSuffix() != null) {
setDefaultCursor(); try {
logger.log(Level.WARNING, "There was an error while deleting: " + selected.getHostSuffix(), ex); dataModel.deleteRecord(selected.getHostSuffix());
} catch (IllegalArgumentException | SQLException ex) {
logger.log(Level.WARNING, "There was an error while deleting: " + selected.getHostSuffix(), ex);
}
}
} }
setDefaultCursor();
refresh();
} }
}//GEN-LAST:event_deleteEntryButtonActionPerformed }//GEN-LAST:event_deleteEntryButtonActionPerformed
@ -399,16 +412,19 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
}//GEN-LAST:event_newEntryButtonActionPerformed }//GEN-LAST:event_newEntryButtonActionPerformed
private void editEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editEntryButtonActionPerformed private void editEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editEntryButtonActionPerformed
DomainCategory selected = getSelected(); List<DomainCategory> selectedItems = getSelected();
if (selected != null && selected.getHostSuffix() != null) { if (CollectionUtils.isNotEmpty(selectedItems)) {
try { DomainCategory selected = selectedItems.get(0);
DomainCategory newCategory = getAddEditValue(selected); if (selected != null && selected.getHostSuffix() != null) {
if (newCategory != null) { try {
runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory)); DomainCategory newCategory = getAddEditValue(selected);
if (newCategory != null) {
runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory));
}
} catch (IllegalArgumentException | SQLException | IOException ex) {
setDefaultCursor();
logger.log(Level.WARNING, "There was an error while editing: " + selected.getHostSuffix(), ex);
} }
} catch (IllegalArgumentException | SQLException | IOException ex) {
setDefaultCursor();
logger.log(Level.WARNING, "There was an error while editing: " + selected.getHostSuffix(), ex);
} }
} }
}//GEN-LAST:event_editEntryButtonActionPerformed }//GEN-LAST:event_editEntryButtonActionPerformed
@ -417,7 +433,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i
"WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage=There was an error importing this json file.", "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage=There was an error importing this json file.",
"WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle=Import Error",}) "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle=Import Error",})
private void importSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importSetButtonActionPerformed private void importSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importSetButtonActionPerformed
fileChooser.setSelectedFile(null); fileChooser.setSelectedFile(new File(""));
int result = fileChooser.showOpenDialog(this); int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) { if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile(); File selectedFile = fileChooser.getSelectedFile();

View File

@ -1,188 +0,0 @@
/* 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<Integer, String> 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<String> getSuffixes(String host) {
if (host == null) {
return null;
}
List<String> hostTokens = Arrays.asList(host.split("\\."));
List<String> 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<String> 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<Integer, String> getCategoryIds(Connection dbConn) throws SQLException {
if (dbConn == null) {
return null;
}
Map<Integer, String> 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;
}
}

View File

@ -83,6 +83,9 @@ class DomainCategoryRunner extends Extract {
private static final Logger logger = Logger.getLogger(DomainCategoryRunner.class.getName()); private static final Logger logger = Logger.getLogger(DomainCategoryRunner.class.getName());
// NOTE: if CustomWebCategorizer ever changes name, this will need to be changed as well.
private static final String CUSTOM_CATEGORIZER_PATH = "org.sleuthkit.autopsy.url.analytics.domaincategorization.CustomWebCategorizer";
/** /**
* Get seconds from epoch from the mapping for the attribute type id. * Get seconds from epoch from the mapping for the attribute type id.
* *
@ -444,7 +447,16 @@ class DomainCategoryRunner extends Extract {
List<DomainCategorizer> foundProviders = lookupList.stream() List<DomainCategorizer> foundProviders = lookupList.stream()
.filter(provider -> provider != null) .filter(provider -> provider != null)
.sorted((a, b) -> a.getClass().getName().compareToIgnoreCase(b.getClass().getName())) .sorted((a, b) -> {
boolean aIsCustom = a.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH);
boolean bIsCustom = b.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH);
if (aIsCustom != bIsCustom) {
// push custom categorizer to top
return -Boolean.compare(aIsCustom, bIsCustom);
}
return a.getClass().getName().compareToIgnoreCase(b.getClass().getName());
})
.collect(Collectors.toList()); .collect(Collectors.toList());
// add the default categorizer last as a last resort // add the default categorizer last as a last resort